I have this application which connects to Facebook. For some reason I'm getting this error:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
This is my code:
The activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
app = (AppVariables)getApplicationContext();
context = getBaseContext();
facebook = app.getFacebook();
facebookRunner = new AsyncFacebookRunner(facebook);
facebookConnection = new FacebookConnection(this);
internetStatus = (TextView)findViewById(R.id.internetStatus);
facebookRunner.request("me", facebookConnection);
}
#Override
public void onFacebookResponse(String response) {
internetStatus.setText(response);
}
This is FacebookConnection Class:
public class FacebookConnection implements RequestListener {
private FacebookConnectionListener listener;
public FacebookConnection (FacebookConnectionListener listener)
{
this.listener = listener;
}
public void updateFacebookListener(String response)
{
listener.onFacebookResponse(response);
}
#Override
public void onComplete(String response, Object state) {
updateFacebookListener(response);
}
#Override
public void onIOException(IOException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFileNotFoundException(FileNotFoundException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onMalformedURLException(MalformedURLException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFacebookError(FacebookError e, Object state) {
// TODO Auto-generated method stub
}
}
And this is my listener interface:
public interface FacebookConnectionListener {
public void onFacebookResponse(String response);
}
What's wrong?
You are using the AsyncFacebookRunner which make all of the API requests in an asynchronous fashion. To do that, it uses a different thread than the main one in which the activity is running.
When the async runner finishes it, it executes your onFacebookResponse method itself, instead of having the main thread executing it.
If your activity is just waiting for the graph request then don't use the AsyncFacebookRunner but the regular Facebook.
If not, then your activity should check if the data was returned, and if so update it in the original thread.
I've never had to do that before, so this is not from my personal experience, and I have not tested it, but from the code in that tutorial it seems like you just need to change your onFacebookResponse to something like this:
public void onFacebookResponse(String response) {
this.runOnUiThread(new Runnable() {
public void run() {
this.internetStatus.setText(response);
}
});
}
Related
I am trying to implement an AsyncTaskLoader running in a fragment and i don't know exactly the reason why onLoadFinished never is called. I am not sure if the context that i pass is the proper one.
This is the basic and custom AsyncTaskLoader:
public static class CustomAsyncLoader extends AsyncTaskLoader<String>
{
public CustomAsyncLoader(Context context) {
super(context);
// do some initializations here
}
#Override
protected void onForceLoad() {
// TODO Auto-generated method stub
super.onForceLoad();
}
#Override
public void deliverResult(String apps) {
}
#Override
protected void onStopLoading() {
// Attempts to cancel the current load task if possible
cancelLoad();
}
#Override
public void onCanceled(String apps) {
super.onCanceled(apps);
}
#Override
public String loadInBackground() {
String result = "";
// ...
// do long running tasks here
// ...
return result;
}
}
Here i will show you the 3 methods overwritted:
#Override
public Loader<String> onCreateLoader(int arg0, Bundle arg1) {
// TODO Auto-generated method stub
return new CustomAsyncLoader(root.getContext());
}
#Override
public void onLoadFinished(Loader<String> arg0, String arg1) {
// TODO Auto-generated method stub
Toast.makeText(mContext, "onLoadFinish", Toast.LENGTH_LONG).show();
}
#Override
public void onLoaderReset(Loader<String> arg0) {
// TODO Auto-generated method stub
}
In the method onResume of my fragment i am calling to init the loader:
getLoaderManager().initLoader(0, null, this).forceLoad();
and the last detail to comment is how the fragment implemented the loader callback:
public class FragmentName extends CustomFragment implements LoaderManager.LoaderCallbacks<String>
Let see if anybody could help me about how implement it. Thanks in advance.
You must call super.deliverResult(apps) in deliverResult method. Otherwise super class of your CustomAsyncLoader won't take care of delivering result to registered listener.
I am trying to show a toast when click on a button that button request's a listener. I am logging out through this button and i want to show toast on loggout completion so i put toast in onComplete method of request Listener. Here is my complete code
HomeActivity which contains button listeners
public class HomeActivity extends Activity implements OnClickListener{
private static final String TAG = "Facebook";
private Button mLogin, mLogout, mShare;
private Facebook facebook;
private AsyncFacebookRunner abRunner;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_home);
//mLogin.setBackgroundColor(Color.BLUE);
// Initialize facebook objects
facebook = new Facebook("479652662068145");
abRunner = new AsyncFacebookRunner(facebook);
// Setup VIews
mLogin= (Button) findViewById(R.id.Login);
mLogout= (Button) findViewById(R.id.Logout);
mLogin.setOnClickListener(this);
mLogout.setOnClickListener(this);
}
#Override
public void onClick(View v){
int id = v.getId();
switch(id){
case R.id.Login:
FacebookLoginDialog login = new FacebookLoginDialog();
facebook.authorize(this, login);
break;
case R.id.Logout:
FacebookLogoutRequest logout = new FacebookLogoutRequest(this);
abRunner.logout(this, logout);
break;
default:
break;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_home, menu);
return true;
}
}
And my RequestListener Code
public class FacebookLogoutRequest implements RequestListener{
private Context context;
public FacebookLogoutRequest (Context context){
this.context= context;
}
public void onComplete(String response, Object state) {
Toast toast = Toast.makeText(context, "You Are Logged Out", Toast.LENGTH_SHORT);
toast.show();
}
#Override
public void onIOException(IOException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFileNotFoundException(FileNotFoundException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onMalformedURLException(MalformedURLException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFacebookError(FacebookError e, Object state) {
// TODO Auto-generated method stub
}
}
Please Help I am stuck here..Thanks
Use Activity instead of Context because context is not for UI reference
public class FacebookLogoutRequest implements RequestListener{
private Activity context;
public FacebookLogoutRequest (Activity context){
this.context= context;
}
public void onComplete(String response, Object state) {
Toast toast = Toast.makeText(context, "You Are Logged Out", Toast.LENGTH_SHORT);
toast.show();
}
#Override
public void onIOException(IOException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFileNotFoundException(FileNotFoundException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onMalformedURLException(MalformedURLException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFacebookError(FacebookError e, Object state) {
// TODO Auto-generated method stub
}
}
Why my eclipse is showing me this error ? I am trying to implement the facebook APi for android but it gives me the above error why ?
They have mentioned in the documentation to place this for logout.
This is the code.
String method = "DELETE";
Bundle params = new Bundle();
/*
* this will revoke 'publish_stream' permission
* Note: If you don't specify a permission then this will de-authorize the application completely.
*/
params.putString("permission", "publish_stream");
mAsyncRunner.request("/me/permissions", params, method, new RevokePermissionListener(), null);
I placed it in the onComplete function of mAsyncRunner.logout
You have to create the class RevokePermissionListener which implements AsyncFacebookRunner.RequestListener(). In the same file, add the following code:
public class RevokePermissionListener implements AsyncFacebookRunner.RequestListener {
#Override
public void onComplete(String response, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onIOException(IOException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFileNotFoundException(FileNotFoundException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onMalformedURLException(MalformedURLException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFacebookError(FacebookError e, Object state) {
// TODO Auto-generated method stub
}
}
I am trying to retrieve my facebook pages information.But when I install and run the app first time it gives me.
{"error":{"message":"An active access token must be used to query information about the current user.","type":"OAuthException","code":2500}}
and the next time when I run the application it returns me the correct result.Why this is happening.I want it to return the data the first time i run the app after installation.
Here's my facebook sdk code:
private String access_Token="";
private final String APP_ID="MY_APP_ID";
private final String[] PERMS = new String[] { "publish_stream","manage_pages" };
private Bundle params=new Bundle();
private SharedPreferences sharedPrefs;
private AsyncFacebookRunner mAsyncRunner;
private Facebook mfacebook;
private TextView view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view=(TextView)findViewById(R.id.Mozi);
this.SetConnection(); //Initialize Fb
this.getAccessToken(); //GetAccessToken
this.CheckSessionExpiry(); //Create Session with permissions if expired
this.RetrieveUserPages();
// this.EnableFBLogout();
}
public void onResume() {
super.onResume();
mfacebook.extendAccessTokenIfNeeded(this, null);
}
private void EnableFBLogout()
{
mAsyncRunner.logout(getApplicationContext(), new RequestListener() {
#Override
public void onComplete(String response, Object state) {
String method = "DELETE";
Bundle params = new Bundle();
/*
* this will revoke 'publish_stream' permission
* Note: If you don't specify a permission then this will de-authorize the application completely.
*/
params.putString("permission", "publish_stream");
mAsyncRunner.request("/me/permissions", params, method,new RequestListener() {
#Override
public void onMalformedURLException(MalformedURLException e, Object state) {
// TODO Auto-generated method stub
Log.e("PerMalform",e.getMessage());
}
#Override
public void onIOException(IOException e, Object state) {
// TODO Auto-generated method stub
Log.e("PerMalform",e.getMessage());
}
#Override
public void onFileNotFoundException(FileNotFoundException e, Object state) {
// TODO Auto-generated method stub
Log.e("PerMalform",e.getMessage());
}
#Override
public void onFacebookError(FacebookError e, Object state) {
// TODO Auto-generated method stub
Log.e("PerMalform",e.getMessage());
}
#Override
public void onComplete(String response, Object state) {
// TODO Auto-generated method stub
Log.e("PerMalform",response);
}
}, null);
}
#Override
public void onIOException(IOException e, Object state) {}
#Override
public void onFileNotFoundException(FileNotFoundException e,
Object state) {}
#Override
public void onMalformedURLException(MalformedURLException e,
Object state) {}
#Override
public void onFacebookError(FacebookError e, Object state) {}
});
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
this.mfacebook.authorizeCallback(requestCode, resultCode, data);
}
private void getAccessToken()
{
sharedPrefs= getPreferences(MODE_PRIVATE);
String access_token = sharedPrefs.getString("access_token", null);
long expires = sharedPrefs.getLong("access_expires", 0);
if(access_token != null) {
mfacebook.setAccessToken(access_token);
}
if(expires != 0) {
mfacebook.setAccessExpires(expires);
}
}
private void CheckSessionExpiry()
{
if(!mfacebook.isSessionValid()) {
mfacebook.authorize(this, this.PERMS , new DialogListener() {
#Override
public void onComplete(Bundle values) {
SharedPreferences.Editor editor = sharedPrefs.edit();
editor.putString("access_token", mfacebook.getAccessToken());
editor.putLong("access_expires", mfacebook.getAccessExpires());
editor.commit();
}
#Override
public void onFacebookError(FacebookError error) {
Log.e("mozi1",error.toString());
}
#Override
public void onError(DialogError e) {
Log.e("mozi2",e.toString());
}
#Override
public void onCancel() {
Log.e("sad","ww");
}
});
}
}
private void SetConnection()
{
this.mfacebook=new Facebook(this.APP_ID);
this.mAsyncRunner=new AsyncFacebookRunner(mfacebook);
}
private void RetrieveUserPages()
{
this.params.putString(Facebook.TOKEN, mfacebook.getAccessToken());
this.mAsyncRunner.request("me/accounts", this.params, "GET", new RequestListener() {
#Override
public void onMalformedURLException(MalformedURLException e, Object state) {
// TODO Auto-generated method stub
Log.e("Malformed",e.getMessage());
}
#Override
public void onIOException(IOException e, Object state) {
// TODO Auto-generated method stub
Log.e("IO",e.getMessage());
}
#Override
public void onFileNotFoundException(FileNotFoundException e, Object state) {
// TODO Auto-generated method stub
Log.e("FNF",e.getMessage());
}
#Override
public void onFacebookError(FacebookError e, Object state) {
// TODO Auto-generated method stub
Log.e("FBERR",e.getMessage());
}
#Override
public void onComplete(String response, Object state) {
// TODO Auto-generated method stub
Log.i("responsefromFB",response); //here the response is an error for the first time and data the second time.
// view.setText(response);
}
}, null);
}
mfacebook.authorize() is asynchronous, which means that code after the authorize() method is run even though authorize() is not completed. Because you call this.RetrieveUserPages() right after mfacebook.authorize(), you are calling
this.params.putString(Facebook.TOKEN, mfacebook.getAccessToken());
before mfacebook.authorize() has completed, so mfacebook.getAcessToken() returns null.
The answer to your issue is to only call this.RetrieveUserPages() in the onComplete method of mfacebook.authorize() to ensure that your access token is set before trying to retrieve it.
Let me know if that helps!
I have 2 activities in my android application. On the first one, I ask the user to login with facebook. after the user logs in, i collect the user data such as email, name and call a new activity passing these parameters to it. below is my facebook authorize method:
public void loginFB(final View v)
{
facebook.authorize(this, new String[] { "email", "read_stream" }, new DialogListener() {
#Override
public void onComplete(Bundle values) {
this.getlogininfo(v);
}
private void getlogininfo(View v) {
// TODO Auto-generated method stub
logininfo(v);
}
#Override
public void onFacebookError(FacebookError error) {}
#Override
public void onError(DialogError e) {}
#Override
public void onCancel() {}
});
}
Below is my logininfo() method:
public void logininfo(final View v){
mAsyncRunner.request("me", new RequestListener(){
#Override
public void onComplete(String response, Object state) {
try{
Log.d("Profile", response.toString());
JSONObject json = Util.parseJson(response);
final String fname1 = json.getString("first_name");
final String lname1 = json.getString("last_name");
final String email = json.getString("email");
Intent fbLogged = new Intent();
Bundle passData = new Bundle();
passData.putString("fname", fname1);
passData.putString("lname", lname1);
passData.putString("email", email);
fbLogged.putExtras(passData);
fbLogged.setClass(v.getContext(), RequestFb.class);
startActivity(fbLogged);
}
catch(JSONException e){
Log.w("This", "eror");
}
}
#Override
public void onIOException(IOException e, Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFileNotFoundException(FileNotFoundException e,
Object state) {
// TODO Auto-generated method stub
}
#Override
public void onMalformedURLException(MalformedURLException e,
Object state) {
// TODO Auto-generated method stub
}
#Override
public void onFacebookError(FacebookError e, Object state) {
// TODO Auto-generated method stub
}
});
}
So, my new activity is starting on OnComplete() of getting the user data.
This works perfectly, but when the user clicks login, and logs in with facebook, the first activity page remains on the screen for a few seconds and then the next activity is called. there is a lag. How can I fix the lag? When the user clicks login and after the login is authorized, it should take the user to directly the second activity. Any suggestions?
Thanks!
It's simple, you are running the fb graph request in a new thread (using the AsyncRunner) but only when that request is completed you start the new activity and that's why you get that "lag".
You should run the graph request in the new activity instead of the first one, something like:
public void loginFB(final View v) {
facebook.authorize(this, new String[] { "email", "read_stream" }, new DialogListener() {
#Override
public void onComplete(Bundle values) {
Intent fbLogged = new Intent(v.getContext(), RequestFb.class);
startActivity(fbLogged);
}
#Override
public void onFacebookError(FacebookError error) {}
#Override
public void onError(DialogError e) {}
#Override
public void onCancel() {}
});
}
public class RequestFb extend Activity {
protected void onCreate(Bundle savedInstanceState) {
Facebook facebook = new Facebook("YOUR_APP_ID");
AsyncFacebookRunner asyncRunner = new AsyncFacebookRunner(facebook);
asyncRunner.request("me", new RequestListener(){
try {
final JSONObject json = Util.parseJson(response);
final String fname1 = json.getString("first_name");
final String lname1 = json.getString("last_name");
final String email = json.getString("email");
runOnUiThread(new Runnable() {
public void run() {
// use the data
}
});
}
catch(JSONException e) {
Log.w("This", "eror");
}
});
}
}