Injecting Javascript into a Webview outside the on

2019-02-21 05:13发布

I have an Android app, running a WebView that loads a certain page, also part of the app. I want to use the Android DatePicker to get a date from the user and set the value on an input inside the page in the WebView.

The DatePickerDialog opens when the user clicks the input, but when i get the return of the date selection, i can't run the loadUrl method to set the value in the input, it gives me an error and the app crashes.

A little bit of code (It's a test code, so don't mind if it is a bit out of context):

Class i provide to the WebView:

public class webViewInterface {
    public void openDatePicker(){
        showDialog(0);
    }
}

Javascript function on the page, called on the click of the input:

function openDatePicker() {
        if (objAndroid != null) objAndroid.openDatePicker();
    }

Listener for the date selection

private DatePickerDialog.OnDateSetListener mDateSetListener =
    new DatePickerDialog.OnDateSetListener() {

        public void onDateSet(DatePicker view, int year, 
                              int monthOfYear, int dayOfMonth) {

            mWebView.loadUrl("javascript:setReturnDate();");
        }
    };

Javascript function that sets a value on the input

function setReturnDate() {
        document.getElementById('txtAgency').value = '9999';
    }

Any ideas?

Thanks in advance.

1条回答
Explosion°爆炸
2楼-- · 2019-02-21 05:38

Thanks @CommonsWare for the comment, i tracked the error and got this:

"android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."

Googled it and got this forum thread

So, in order to update a view in the UI at runtime and outside it's thread, you have to use a Handler, that receive messages and execute actions accordingly.

So i implemented a Handler for the Activity, that gets a message with an int identifier and the data i want to update into the webview:

final Handler handler = new Handler() {
    public void handleMessage(Message msg) {

        int cod = msg.getData().getInt("messageType");
        switch(cod){
            case 1:
                String date = msg.getData().getString("datePickerValue");
                mWebView.loadUrl("javascript:setReturnDate('" + mId + "', '" + date + "');");
                break;
            case 2:
                showDialog(0);
                break;
        }

    }
};

I had to change the openDatePicker method of the class to use the Handler, as well as create an input parameter, which is the id of the control in the page:

public void openDatePicker(String id){

        mId = id; //mId is a string declared in the Activity scope, so it can be used anywhere
        Message msg = new Message();
        Bundle b = new Bundle();
        b.putInt("messageType", 2);

        msg.setData(b);
        handler.sendMessage(msg);
    }

Rewrote the DatePicker listener for the date selection, to get the data and send a message to the Handler:

private DatePickerDialog.OnDateSetListener mDateSetListener =
    new DatePickerDialog.OnDateSetListener() {

        public void onDateSet(DatePicker view, int year, 
                              int monthOfYear, int dayOfMonth) {

            Message msg = new Message();
            Bundle b = new Bundle();
            b.putInt("messageType", 1);
            b.putString("datePickerValue", 
                    String.format("%02d", dayOfMonth) + "/" + 
                    String.format("%02d", (monthOfYear + 1)) + "/" + 
                    String.valueOf(year));
            msg.setData(b);
            handler.sendMessage(msg);
        }
    };

In the webpage, the js functions got out like this:

function openDatePicker(id) {
        if (objAndroid != null) objAndroid.openDatePicker(id);
    }

    function setReturnDate(id, date) {
        var obj = document.getElementById(id);
        if (obj != null)
            obj.value = date;
    }

And finally, the input (TextBox), got like this:

TextBox obj = FindControl("txtDt") as TextBox;

            if (obj != null)
            {
                obj.Attributes.Add("readonly", "readonly");
                obj.Attributes.Add("OnClick", "javascript:openDatePicker(this.id)");
            }
查看更多
登录 后发表回答