I made a simple RPC mechanism apps for android and I faced a problem that I can not go back to the UI Thread from RPC class.
Basically I have 3 classes(ServerActivity,ServerView and ServiceImplementation), I created 3 classes because I use RPC and Protocol Buffer for drawing.
Server Activity :
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_sv = new ServerView(this);
setContentView(_sv);
rpcConnectionFactory = SocketRpcConnectionFactories.createServerRpcConnectionFactory(SERVER_PORT);
int nThreadPool = 1;
server = new RpcServer(rpcConnectionFactory, Executors.newFixedThreadPool(nThreadPool), true);
server.registerBlockingService(Service.newReflectiveBlockingService(new ServiceImpl(myServiceHandler)));
server.run();
}
Handler myServiceHandler = new Handler() {
public void handleMessage(Message msg) {
Log.i("Handler", "Handler IN");
_sv.set(msg.what); /*To communicate with the view*/
super.handleMessage(msg);
}
};
ServiceImplementation :
public CanvasServiceImpl(Handler mActivity) {
backToUIThread = mActivity;
}
public Response drawCircle(RpcController controller, Circle1 request)
throws ServiceException {
android.os.Message message = new android.os.Message();
message.what = 1;
ImplHandler.sendMessage(message);
Response response = Response.newBuilder().setResult("drawCircle Success").build();
return response;
}
I can not reach my UI Thread. Does anybody know why ?
Thanks,
Robert
Instead of
ImplHandler.sendMessage(message);
use
backToUIThread.sendMessage(message);
Related
here is my hander:
public Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String mmsg = msg.getData().getByteArray("msg").toString();
Toast.makeText(clientActivity.this, mmsg,Toast.LENGTH_LONG).show();
}
};
I send data from separate thread:
public class client implements Runnable
{
private void showtoast(byte[] msgtoshow){
try {
Bundle mbundle = new Bundle();
Message greeting = new Message();
mbundle.putByteArray("msg", msgtoshow);
greeting.setData(mbundle);
handler.sendMessage(greeting);
}catch(Exception e){
Log.d("error",e.getMessage());
}
}
public void run()
{
msgstrng = "this is supposed to be some text";
showtoast(msgstrng.getBytes());
}
}
Instead of line that I send in msgstng I toast some [B#411dd11] which is always different. I guess it's timestamp or smth. how to get that String value msgstng?
Actually more important for me is to get bytes array, as I'll send bitmaps from socket to UI if i learn this issue
The problem seems to be the way you are getting the string in your Handler.
Try something like this:
String mmsg = new String(msg.getData().getByteArray("msg"));
I try to send message from class that handles background process for my application communicates with server and prace responce than serialize the objects and send it to activity that visualise results. Activity has a public handler.
public final Handler _handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
String smsg = msg.getData().toString();
if (!smsg.contains("endResult")) {
Log.d("New 10 a","New 10");
deviceBasicList a = (deviceBasicList) msg.getData()
.getSerializable("devices");
List<deviceBasic> basicList = a.getList();
setDeviceList(basicList);
} else {
hideLoadingDialog();
}
super.handleMessage(msg);
}
};
In the background process class I have an instance of the activity
this.searchActivity = a;
After I parce the objects im sending message :
private void sendDeviceList() {
if (!this.deviceList.isEmpty()) {
deviceBasicList basicList = new deviceBasicList(this.deviceList);
Message msg = new Message();
msg.setTarget(this.searchActivity._handler);
Bundle bundle = new Bundle();
bundle.putSerializable("devices", basicList);
msg.setData(bundle);
this.searchActivity._handler.sendMessage(msg);
this.deviceList.clear();
}
}
Because of the large amounts of data Im sending information for objects 10 by 10.
The result is that In the background class I send exact amount of messages I should, but in the activity im reciveing about 5 times more that I have send.
Here is the problem:
private void doSomething() {
String[][] data = new String[h][w];
Message msg = null;
Thread t = new Thread() {
public void run() {
for(int i=0; i<max; i++) {
data = doLongCalculationOnBackground(i);
msg = messageHandler.obtainMessage();
msg.obj = data;
messageHandler.sendMessage(msg);
}
}
};
t.start();
}
private Handler messageHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
doUpdateUI(msg.obj); // error right here!!!
}
};
private doUpdateUI(String[][] data) {
// do update UI work.
}
Eclipse alerts that doUpdateUI(msg.obj) is not applicable for the arguments (Object).
So how can i obtain the string matrix sent by Message object? Please don't suggest me use Async Task.
I'm stupid, just cast argument msg.obj to String[][]:
doUpdateUI((String[][]) msg.obj);
I seem to be having trouble with updating a TextView from a thread. I have a GameConnection class (which manages a socket connection) which I want to use across activities. It calls a local "onMessage", which then uses the target handler to call dispatch Message. The "Handler" in this case, is in my GameBrowser activity.
Here's code from the GameConnection class.
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
String message = "".intern();
// as a newline character is read, we interpret it as a message
while ((message = in.readLine()) != null && isConnected){
onMessage(message);
}
As said above, a local method "onMessage" method handles dispatching of the message.
private void onMessage(String message){
... // create message from String
handler.dispatchMessage( msg );
}
However, when I get the response in the GameBrowser class, I get a CalledFromWrongThreadException . Initially, I was using a callback method, which of course wasn't working. So, after some research, I've found that I have to use a Handler, but I can't seem to get it right.
public class GameBrowser extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(C.tag, "GameBrowser.onCreate addr:" + this);
handler = new Handler(new HandlerCallback());
connection.addMessageListener(handler);
connection.connect();
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
setContentView(R.layout.game_browser);
}
private class HandlerCallback implements Callback{
#Override
public boolean handleMessage(Message msg) {
if (txtGameLabel == null){
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
}
String message = msg.getData().getString("message");
Log.d(C.tag, "GameBrowser recieved message " + message);
txtGameLabel.setText("Data: " + message);
return true;
}
}
}
I figured out what I was doing wrong. Instead of calling the handler from the socket thread, I used a callback, then used Runnable to post to the handler in the GameConnection class. When onMessage executes "run", which executes "updateTextField", we're back in the main thread.
#Override
public void onMessage(final String message) {
handler.post(new Runnable(){
#Override
public void run() {
updateTextField(message);
}
});
}
private void updateTextField(String message){
if (txtGameLabel == null)
txtGameLabel = (TextView)findViewById( R.id.txtGamesLabel);
txtGameLabel.setText(message);
}
In Handler, we can pass some data from a background thread to the UI thread like this:
private void someBackgroundThreadOperation() {
final String data = "hello";
handler.post(new Runnable() {
public void run() {
Log.d(TAG, "Message from bg thread: " + data);
}
}
}
If we use the above, we cannot then use Handler.removeCallbacks(Runnable r), because we won't have references to any of the anonymous runnables we created above.
We could create a single Runnable instance, and post that to the handler, but it won't allow us to pass any data through:
private void someBackgroundThreadOperation() {
String data = "hello";
handler.post(mRunnable); // can't pass 'data' through.
}
private Runnable mRunnable = new Runnable() {
public void run() {
Log.d(TAG, "What data?");
}
}
We can however use the Handler.removeCallbacks(mRunnable) method then, since we have a reference to it.
I know I can setup some synchronization myself to get the second method working, but I'm wondering if Handler offers any utility for passing data through to a single referenced Runnable, almost like in the example above?
The closest I can think of from browsing the docs is to use something like:
private void someUiThreadSetup() {
mHandler = new Handler(new Callback() {
#Override
public boolean handleMessage(Message msg) {
String data = (String)msg.obj;
return false;
}
});
}
private void someBackgroundThreadOperation() {
String data = "please work";
Message msg = mHandler.obtain(0, data);
mHandler.sendMessage(msg);
}
private void cleanup() {
mHandler.removeMessages(0);
}
Is this the proper way of doing it?
Thanks
IMHO, the pattern you seek is packaged as the AsyncTask class.