无法正确退出或完成我的应用程序android(活动)

时间:2014-09-25 20:01:27

标签: java android exit activity-finish

下面的应用程序在启动时将视频上传到Dropbox,我在onCreate的末尾添加了finish()以在完成上传时退出应用程序,但我得到“不幸的是,DBRoulette已停止。”。

在Eclipse LogCat中:

Activity com.dropbox.android.sample.DBRoulette has leaked window  com.android.internal.policy.impl.PhoneWindow$DecorView@42a7ee38 that was originally added here
android.view.WindowLeaked: Activity com.dropbox.android.sample.DBRoulette has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@42a7ee38 that was originally added here

我有可能导致问题的进度查看器,但我不知道如何解决它! 我想要做的是在上传完成后自动关闭应用程序。

DBRoulette.java

@SuppressLint("SimpleDateFormat")
public class DBRoulette extends Activity {
private static final String TAG = "DBRoulette";

final static private String APP_KEY = "<My APP_KEY>";
final static private String APP_SECRET = "<My APP_SECRET>";

// You don't need to change these, leave them alone.
final static private String ACCOUNT_PREFS_NAME = "prefs";
final static private String ACCESS_KEY_NAME = "ACCESS_KEY";
final static private String ACCESS_SECRET_NAME = "ACCESS_SECRET";

private static final boolean USE_OAUTH1 = false;

DropboxAPI<AndroidAuthSession> mApi;

private boolean mLoggedIn;

// Android widgets
private Button mSubmit;
private RelativeLayout mDisplay;
private Button  mGallery;

private ImageView mImage;

private final String PHOTO_DIR = "/Motion/";

@SuppressWarnings("unused")
final static private int NEW_PICTURE = 50;
private String mCameraFileName;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        mCameraFileName = savedInstanceState.getString("mCameraFileName");          
    }

    // We create a new AuthSession so that we can use the Dropbox API.
    AndroidAuthSession session = buildSession();
    mApi = new DropboxAPI<AndroidAuthSession>(session);

    // Basic Android widgets
    setContentView(R.layout.main);

    checkAppKeySetup();

    mSubmit = (Button) findViewById(R.id.auth_button);

    mSubmit.setOnClickListener(new OnClickListener() {
        @SuppressWarnings("deprecation")
        public void onClick(View v) {
            // This logs you out if you're logged in, or vice versa
            if (mLoggedIn) {
                logOut();
            } else {
                // Start the remote authentication
                if (USE_OAUTH1) {
                    mApi.getSession().startAuthentication(DBRoulette.this);
                } else {
                    mApi.getSession().startOAuth2Authentication(
                            DBRoulette.this);


                }
            }
        }
    });

    mDisplay = (RelativeLayout) findViewById(R.id.logged_in_display);

    // This is where a photo is displayed
    mImage = (ImageView) findViewById(R.id.image_view);


    File outFile = new File("/mnt/sdcard/ipwebcam_videos/video.mov");

    mCameraFileName = outFile.toString();
    UploadPicture upload = new UploadPicture(DBRoulette.this, mApi, PHOTO_DIR,outFile);
    upload.execute();



    // Display the proper UI state if logged in or not
    setLoggedIn(mApi.getSession().isLinked());
    finish();

}

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("mCameraFileName", mCameraFileName);
    super.onSaveInstanceState(outState);
}

@Override
protected void onResume() {
    super.onResume();
    AndroidAuthSession session = mApi.getSession();
    if (session.authenticationSuccessful()) {
        try {
            session.finishAuthentication();
            storeAuth(session);
            setLoggedIn(true);
        } catch (IllegalStateException e) {
            showToast("Couldn't authenticate with Dropbox:"
                    + e.getLocalizedMessage());
            Log.i(TAG, "Error authenticating", e);
        }
    }
}


private void logOut() {
    // Remove credentials from the session
    mApi.getSession().unlink();
    clearKeys();
    // Change UI state to display logged out version
    setLoggedIn(false);
}
@SuppressWarnings("deprecation")
public String getRealPathFromURI(Uri contentUri) 
{
     String[] proj = { MediaStore.Audio.Media.DATA };
     Cursor cursor = managedQuery(contentUri, proj, null, null, null);
     int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
     cursor.moveToFirst();
     return cursor.getString(column_index);
}

private void setLoggedIn(boolean loggedIn) {
    mLoggedIn = loggedIn;
    if (loggedIn) {
        mSubmit.setText("Logout from Dropbox");
        mDisplay.setVisibility(View.VISIBLE);
    } else {
        mSubmit.setText("Login with Dropbox");
        mDisplay.setVisibility(View.GONE);
        mImage.setImageDrawable(null);
    }
}

private void checkAppKeySetup() {
    // Check to make sure that we have a valid app key
    if (APP_KEY.startsWith("CHANGE") || APP_SECRET.startsWith("CHANGE")) {
        showToast("You must apply for an app key and secret from developers.dropbox.com, and add them to the DBRoulette ap before trying it.");
        finish();
        return;
    }

    // Check if the app has set up its manifest properly.
    Intent testIntent = new Intent(Intent.ACTION_VIEW);
    String scheme = "db-" + APP_KEY;
    String uri = scheme + "://" + AuthActivity.AUTH_VERSION + "/test";
    testIntent.setData(Uri.parse(uri));
    PackageManager pm = getPackageManager();
    if (0 == pm.queryIntentActivities(testIntent, 0).size()) {
        showToast("URL scheme in your app's "
                + "manifest is not set up correctly. You should have a "
                + "com.dropbox.client2.android.AuthActivity with the "
                + "scheme: " + scheme);
        finish();
    }
}

private void showToast(String msg) {
    Toast error = Toast.makeText(this, msg, Toast.LENGTH_LONG);
    error.show();
}

/**
 * Shows keeping the access keys returned from Trusted Authenticator in a
 * local store, rather than storing user name & password, and
 * re-authenticating each time (which is not to be done, ever).
 */
private void loadAuth(AndroidAuthSession session) {
    SharedPreferences prefs = getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
    String key = prefs.getString(ACCESS_KEY_NAME, null);
    String secret = prefs.getString(ACCESS_SECRET_NAME, null);
    if (key == null || secret == null || key.length() == 0
            || secret.length() == 0)
        return;

    if (key.equals("oauth2:")) {
        // If the key is set to "oauth2:", then we can assume the token is
        // for OAuth 2.
        session.setOAuth2AccessToken(secret);
    } else {
        // Still support using old OAuth 1 tokens.
        session.setAccessTokenPair(new AccessTokenPair(key, secret));
    }
}

/**
 * Shows keeping the access keys returned from Trusted Authenticator in a
 * local store, rather than storing user name & password, and
 * re-authenticating each time (which is not to be done, ever).
 */
private void storeAuth(AndroidAuthSession session) {
    // Store the OAuth 2 access token, if there is one.
    String oauth2AccessToken = session.getOAuth2AccessToken();
    if (oauth2AccessToken != null) {
        SharedPreferences prefs = getSharedPreferences(ACCOUNT_PREFS_NAME,
                0);
        Editor edit = prefs.edit();
        edit.putString(ACCESS_KEY_NAME, "oauth2:");
        edit.putString(ACCESS_SECRET_NAME, oauth2AccessToken);
        edit.commit();
        return;
    }
    // Store the OAuth 1 access token, if there is one. This is only
    // necessary if
    // you're still using OAuth 1.
    AccessTokenPair oauth1AccessToken = session.getAccessTokenPair();
    if (oauth1AccessToken != null) {
        SharedPreferences prefs = getSharedPreferences(ACCOUNT_PREFS_NAME,
                0);
        Editor edit = prefs.edit();
        edit.putString(ACCESS_KEY_NAME, oauth1AccessToken.key);
        edit.putString(ACCESS_SECRET_NAME, oauth1AccessToken.secret);
        edit.commit();
        return;
    }
}

private void clearKeys() {
    SharedPreferences prefs = getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
    Editor edit = prefs.edit();
    edit.clear();
    edit.commit();
}

private AndroidAuthSession buildSession() {
    AppKeyPair appKeyPair = new AppKeyPair(APP_KEY, APP_SECRET);

    AndroidAuthSession session = new AndroidAuthSession(appKeyPair);
    loadAuth(session);
    return session;
}
}

UploadPicture.java

public class UploadPicture extends AsyncTask<Void, Long, Boolean> {

private DropboxAPI<?> mApi;
private String mPath;
private File mFile;

private long mFileLen;
private UploadRequest mRequest;
private Context mContext;
private final ProgressDialog mDialog;

private String mErrorMsg;
private File outFiles;


public UploadPicture(Context context, DropboxAPI<?> api, String dropboxPath,
        File file) {
    // We set the context this way so we don't accidentally leak activities
    mContext = context.getApplicationContext();

    mFileLen = file.length();
    mApi = api;
    mPath = dropboxPath;
    mFile = file;
    Date dates = new Date();
    DateFormat dfs = new SimpleDateFormat("yyyyMMdd-kkmmss");

    String newPicFiles = dfs.format(dates) + ".mov";
    String outPaths = new File(Environment
            .getExternalStorageDirectory(), newPicFiles).getPath();
    outFiles = new File(outPaths);
    mDialog = new ProgressDialog(context);
    mDialog.setMax(100);
    mDialog.setMessage("Uploading " + outFiles.getName());
    mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    mDialog.setProgress(0);
    mDialog.setButton(ProgressDialog.BUTTON_POSITIVE, "Cancel", new OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // This will cancel the putFile operation
            mRequest.abort();
        }
    });
    mDialog.show();
}

@Override
protected Boolean doInBackground(Void... params) {
    try {
        // By creating a request, we get a handle to the putFile operation,
        // so we can cancel it later if we want to
        FileInputStream fis = new FileInputStream(mFile);
        String path = mPath + outFiles.getName();
        mRequest = mApi.putFileOverwriteRequest(path, fis, mFile.length(),
                new ProgressListener() {
            @Override
            public long progressInterval() {
                // Update the progress bar every half-second or so
                return 500;
            }

            @Override
            public void onProgress(long bytes, long total) {
                publishProgress(bytes);
            }
        });

        if (mRequest != null) {
            mRequest.upload();
            return true;
        }

    } catch (DropboxUnlinkedException e) {
        // This session wasn't authenticated properly or user unlinked
        mErrorMsg = "This app wasn't authenticated properly.";
    } catch (DropboxFileSizeException e) {
        // File size too big to upload via the API
        mErrorMsg = "This file is too big to upload";
    } catch (DropboxPartialFileException e) {
        // We canceled the operation
        mErrorMsg = "Upload canceled";
    } catch (DropboxServerException e) {
        // Server-side exception.  These are examples of what could happen,
        // but we don't do anything special with them here.
        if (e.error == DropboxServerException._401_UNAUTHORIZED) {
            // Unauthorized, so we should unlink them.  You may want to
            // automatically log the user out in this case.
        } else if (e.error == DropboxServerException._403_FORBIDDEN) {
            // Not allowed to access this
        } else if (e.error == DropboxServerException._404_NOT_FOUND) {
            // path not found (or if it was the thumbnail, can't be
            // thumbnailed)
        } else if (e.error == DropboxServerException._507_INSUFFICIENT_STORAGE) {
            // user is over quota
        } else {
            // Something else
        }
        // This gets the Dropbox error, translated into the user's language
        mErrorMsg = e.body.userError;
        if (mErrorMsg == null) {
            mErrorMsg = e.body.error;
        }
    } catch (DropboxIOException e) {
        // Happens all the time, probably want to retry automatically.
        mErrorMsg = "Network error.  Try again.";
    } catch (DropboxParseException e) {
        // Probably due to Dropbox server restarting, should retry
        mErrorMsg = "Dropbox error.  Try again.";
    } catch (DropboxException e) {
        // Unknown error
        mErrorMsg = "Unknown error.  Try again.";
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    return false;
}

@Override
protected void onProgressUpdate(Long... progress) {
    int percent = (int)(100.0*(double)progress[0]/mFileLen + 0.5);
    mDialog.setProgress(percent);
}

@Override
protected void onPostExecute(Boolean result) {
    mDialog.dismiss();
    if (result) {
        showToast("Image successfully uploaded");
    } else {
        showToast(mErrorMsg);
    }
}

private void showToast(String msg) {
    Toast error = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
    error.show();
}
}

1 个答案:

答案 0 :(得分:1)

您的问题是,您在活动结束后尝试更新Activity的用户界面。

首先,你踢了一个AsyncTask,它将进度更新发布到UI线程。然后,在任务完成之前,您调用finish()。调用finish()后对Activity的UI进行的任何更新都可能导致异常,导致窗口泄漏问题等。

如果您希望在执行AsyncTask时有任何UI行为,则不希望在任务完成之前完成()Activity。

要实现这一点,您可以在onPostExecute中包含一个回调函数,该回调告诉活动在AsyncTask完成后可以完成。

我就是这样做的:

  1. 更改UploadPicture的签名:

    final Activity callingActivity;
    public UploadPicture(final Activity callingActivity, DropboxAPI api, String dropboxPath, File file) {
        Context mContext = callingActivity.getApplicationContext();
        this.callingActivity = callingActivity;

  2. 将结束通话添加到onPostExecute

    @Override
    protected void onPostExecute(Boolean result) {
        mDialog.dismiss();
        if (result) {
            showToast("Image successfully uploaded");
        } else {
            showToast(mErrorMsg);
        }
        callingActivity.finish(); //Finish activity only once you are done
    }