I've been looking into communicating between the main thread and a WebView, but whatever I try, nothing ever seems to happen. And unfortunately I don't really have a Javascript debug console at the ready in Android :-/
It's probably some stupid small mistake again, but I can't find the problem. It simply doesn't do anything. I don't see any errors or whatever.
#SuppressLint("SetJavaScriptEnabled")
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
WebView web = (WebView) findViewById (R.id.web);
web.getSettings ().setJavaScriptEnabled (true);
web.setWebChromeClient (new WebChromeClient ());
JavaScriptInterface jsInterface = new JavaScriptInterface (this);
web.addJavascriptInterface (jsInterface, "accessAndroid");
}
public class JavaScriptInterface
{
Context context;
JavaScriptInterface (Context c)
{
context = c;
}
public void gotoArticle (int id)
{
jsHandler.post
(
new Runnable ()
{
public void run ()
{
//TODO//
}
}
);
}
public void showDialog (final String message)
{
jsHandler.post
(
new Runnable ()
{
public void run ()
{
showDialog (message);
}
}
);
}
}
When running alert ('Something'); from the loaded webpage, all is fine. But dlg ('Something'); doesn't do anything.
Related
I'm working on an alarm clock and I can't figure out how to sendEvent to React Native from MainActivity. This is what I managed to do so far:
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
ReactInstanceManager mReactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactApplicationContext context = (ReactApplicationContext) mReactInstanceManager.getCurrentReactContext();
if (context == null) {
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // doesn't work. Should run after alarm manager starts app which previously had been killed
}
}
}
});
} else {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // works and sends event only when app was left open
}
}
}
super.onCreate(savedInstanceState);
}
The code works only If app is left open and alarm manager restarts app itself. If I close the app and alarm manager starts it then it seems that only startAlarm function (it has sound effect) is beeing triggered..
No matter what I do whether I put sendEvent function inside Mainactivity or elsewhere (e.g. external module) it simply won't send event if I close the app. I also tried getReactInstanceManager().getCurrentReactContext() combined with while from this question Send data from Android activity to React Native to no avail.
Also tried to create bolean beeing set to true onCreate and then send event onStart or onRestart. Also to no avail.
Any suggestions?
EDIT: Here is how sendEvent function looks like:
public final void sendEvent(String eventName, boolean isAlarmOn) {
getReactInstanceManager().getCurrentReactContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, isAlarmOn);
}
SOLUTION
Well I think that the answer is not to use sendEvent method onCreate because (I might be wrong) listener seems to be initialized after the event had been sent. So nothing is going to listen to this event.
It seems to work pretty well inside onStart, onRestart, onPause though.
What can we do? React Native provides ReactActivityDelegate with initial props. And it does the job!
ReactActivityDelegate in MainActivity should look as below:
public class ActivityDelegate extends ReactActivityDelegate {
private Bundle mInitialProps = null;
private final #Nullable Activity mActivity;
public ActivityDelegate(Activity activity, String mainComponentName) {
super(activity, mainComponentName);
this.mActivity = activity;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
mInitialProps.putBoolean("alarmOn", true);
}
}
super.onCreate(savedInstanceState);
}
#Override
protected Bundle getLaunchOptions() {
return mInitialProps;
}
};
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ActivityDelegate(this, getMainComponentName());
}
Then in your main app component (usually index.android.js) call your propTypes and use them to run your code:
static propTypes = {
alarmOn: PropTypes.boolean
}
componentDidMount() {
if (this.props.alarmOn === true) {
// your code
}
}
Voila!
You can find full example here: https://github.com/vasyl91/react-native-android-alarms
I am trying to get a simple Runnable to execute some code every few seconds, but although I can get it to execute, I cant get it to stop. The code below shows 2 calls startDbChecking() and stopDbChecking(), I have just placed them in the code block to show what I'm attempting - not how the code is set up.
public class MainActivity extends TabActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startDbChecking(); // will run this no problem
stopDbChecking(); // but will not stop
}
public void startDbChecking() {
handler.post(runnableCode);
}
public void stopDbChecking() {
handler.removeCallbacks(runnableCode);
}
private Runnable runnableCode = new Runnable() {
#Override
public void run() {
// Do something here on the main thread
System.out.println("OK");
handler.postDelayed(runnableCode, 2000);
}
};
}
Try this in your Activity: to stop the runnable
protected void onStop() {
super.onStop();
handler.removeCallbacks(runnableCode);
}
I searched for a solution and couldn't find one so I'll ask here:
I'm trying to use setText command in the mainActivity, Until now I've used:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
textViewPrograss.setText(finalI + "");
}
});
now I'm trying to do the same thing, but from another class so i cant use:MainActivity.this.
I was trying to use code i found on another question with no success,This is the code:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Log.d("UI thread", "I am the UI thread");
}});
Any suggestions?
This (the second code sample from your question) is the correct way to access UI Thread from random location, although you should always try to have a Context to do this :-)
new Handler(Looper.getMainLooper()).post(
new Runnable() {
#Override
public void run() {
Log.d("UI thread", "I am the UI thread");
}});
It does work, and if it does not, check if you have debug logs enabled in your debugger ^^
You can use this snippet
textView.post(new Runnable() {
#Override
public void run() {
textView.setText("Text");
}
});
Try this just pass the context to other class and then use it like this
((Activity)context).runOnUiThread(new Runnable() {
public void run() {
textViewPrograss.setText(finalI + "");
}
});
I suggest you tu use a BroadcastReceiver in the MainActivity. Register a new receiver with a specific action and send an Intent with that action from "another class". The MainActivity will receive the notification and can edit the TextView content in a clean way
MainActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// update your text view
String text = intent.getStringExtra("text");
}
};
registerReceiver(mReceiver, new IntentFilter(MY_ACTION));
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
Another class:
Intent intent = new Intent(MY_ACTION);
intent.putExtra("text", "Your wonderful text");
// take a context reference (e.g. mContext) if you don't have a getContext() method
getContext().sendBroadcast(intent);
This is my solution (using MVVM, so no business logic in activity), run this in your class (viewmodel in my case):
runOnUiThread(() -> methodToExecute());
here is the method implementation (I have this in my base viewmodel):
private Handler messageHandler;
protected void runOnUiThread(Runnable action) {
messageHandler.post(action);
}
Don't forget to init messageHandler:
messageHandler = new Handler(Looper.getMainLooper());
Is it possible to create a background thread using andriodannotations thats starts in one activity and finish in another activity.
here is what i thought migh have worked
ActivityA
public static LoadingDialog LoadingScreen = new LoadingDialog();
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//.....
LoadingScreen.CreateDialog(context);
}
Background Class
public class LoadingDialog
{
private Dialog loader_dialog;
#Background
public void CreateDialog(Context mContext)
{
loader_dialog = new Dialog(mContext,android.R.style.Theme_Black_NoTitleBar_Fullscreen);
loader_dialog.setContentView(R.layout.loading_screen);
loader_dialog.show();
}
public void Remove()
{
loader_dialog.dismiss();
}
}
the dialog displays correctly but when I finish() activityA to start activityB the thread appears to be killed to and i get a black screen. Any help on this would be much appreciated.
I have an android app I am just experimenting things on and I cannot seem to figure out why my app force closes when I update a TextView via a while loop. When I comment out the updateText method it runs fine.
public class GameThread extends Thread {
Thread t;
private int i;
private boolean running;
private long sleepTime;
GameView gv;
public GameThread() {
t = new Thread(this);
t.start();
i = 0;
sleepTime = 1000;
}
public void initView(GameView v) {
this.gv = v;
}
public void setRunning(boolean b) {
this.running = b;
}
public boolean getRunning() {
return running;
}
public void run() {
while(running) {
i++;
update();
try {
t.sleep(sleepTime);
} catch(InterruptedException e) {
}
}
}
public void update() {
gv.setText(i); // when this is uncommented, it causes force close
Log.v("Semajhan", "i = " + i);
}
public class GameView extends LinearLayout {
public TextView tv;
public GameView(Context c) {
super(c);
this.setBackgroundColor(Color.WHITE);
tv = new TextView(c);
tv.setTextColor(Color.BLACK);
tv.setTextSize(20);
this.addView(tv);
}
public void setText(int i) {
tv.setText("i count: " + i);
}
public class Exp extends Activity {
GameThread t;
GameView v;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
v = new GameView(this);
setContentView(v);
t = new GameThread();
t.setRunning(true);
t.initView(v);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (t.getRunning() == true) {
t.setRunning(false);
Log.v("Semajhan", "STOPPED");
} else {
t.setRunning(true);
Log.v("Semajhan", "RESTART");
}
}
return true;
}
protected void onDestroy() {
Log.v("Semajhan", "DESTROYING");
super.onDestroy();
}
protected void onStop() {
Log.v("Semajhan", "Stopping");
super.onStop();
}
I though i'd post the whole app since it is relatively small and so that I could get some help without confusion.
First, when you get a Force Close dialog, use adb logcat, DDMS, or the DDMS perspective in Eclipse to examine LogCat and look at the stack trace associated with your crash.
In this case, your exception will be something to the effect of "Cannot modify the user interface from a non-UI thread". You are attempting to call setText() from a background thread, which is not supported.
Using a GameThread makes sense if you are using 2D/3D graphics. It is not an appropriate pattern for widget-based applications. There are many, many, many, many examples that demonstrate how to create widget-based applications without the use of a GameThread.
You have to call it from the UI thread.
For more info check: Painless Threading .
If you decide to use a Handler, the easiest solution for you will be to:
Extend a View, override it's onDraw , in it draw the game objects, after you have calculated the game data for them first of course
The Handler: (in your Activity)
private Handler playHandler = new Handler() {
public void handleMessage(Message msg) {
gameView.postInvalidate(); // gameView is the View that you extended
}
};
The game thread has a simple
Message.obtain(playHandler).sendToTarget();
In 2 words, the View is responsible for the drawing (you can move the calculations in a separate class, and call it before the onDraw), the thread is responsible only for scheduled calls to the Handler, and the Handler is responsible only to tell the View to redraw itself.
You cannot update the UI of your app outside of the UI Thread, which is the 'main' thread you start in. In onCreate(Context) of you app, you are creating the game thread object, which is what is doing the updating of your UI.
You should use a Handler:
http://developer.android.com/reference/android/os/Handler.html