2015-01-13 51 views
0

在調用onDestroy()後,logcat顯示Activity已泄漏服務連接錯誤。這裏是logcat的一個部分:如何擺脫活動已泄漏服務連接錯誤

01-13 18:14:50.281: D/MYLOG(16984): in passStop() 
01-13 18:14:51.471: D/MYLOG(16984): onPause() called 
01-13 18:14:52.001: D/MYLOG(16984): onDestroy() called 
01-13 18:14:52.021: E/ActivityThread(16984): Activity com.example.mypassthrough.MainActivity has leaked ServiceConnection [email protected] that was originally bound here 
01-13 18:14:52.021: E/ActivityThread(16984): android.app.ServiceConnectionLeaked: Activity com.example.mypassthrough.MainActivity has leaked ServiceConnection [email protected] that was originally bound here 
01-13 18:14:52.021: E/ActivityThread(16984): at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:970) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:864) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1569) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.app.ContextImpl.bindService(ContextImpl.java:1552) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.content.ContextWrapper.bindService(ContextWrapper.java:517) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.bluetooth.BluetoothA2dp.doBind(BluetoothA2dp.java:165) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.bluetooth.BluetoothA2dp.<init>(BluetoothA2dp.java:158) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.bluetooth.BluetoothAdapter.getProfileProxy(BluetoothAdapter.java:1364) 
01-13 18:14:52.021: E/ActivityThread(16984): at com.example.mypassthrough.BluetoothA2DPRequester.request(BluetoothA2DPRequester.java:52) 
01-13 18:14:52.021: E/ActivityThread(16984): at com.example.mypassthrough.MainActivity.btnA2dpStart_onClick(MainActivity.java:307) 
01-13 18:14:52.021: E/ActivityThread(16984): at java.lang.reflect.Method.invokeNative(Native Method) 
01-13 18:14:52.021: E/ActivityThread(16984): at java.lang.reflect.Method.invoke(Method.java:515) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.view.View$1.onClick(View.java:3818) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.view.View.performClick(View.java:4438) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.view.View$PerformClick.run(View.java:18422) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.os.Handler.handleCallback(Handler.java:733) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.os.Handler.dispatchMessage(Handler.java:95) 
01-13 18:14:52.021: E/ActivityThread(16984): at android.os.Looper.loop(Looper.java:136) 

這是在MainActivity文件:

// public class MainActivity extends Activity { 
// public class MainActivity extends Activity implements BluetoothBroadcastReceiver.Callback, BluetoothA2DPRequester.Callback { 
public class MainActivity extends Activity implements BluetoothA2DPRequester.Callback { 

    AudioManager am = null; 
    AudioRecord record =null; 
    AudioTrack track =null; 
    BluetoothHeadset mBluetoothHeadset; 
    // BluetoothA2dp mBluetoothSpeaker; 
    BluetoothDevice device; 
    BluetoothDevice mConnectedHeadset; 

    ConnectionClass mConnectionClass; 

    final int SAMPLE_FREQUENCY = 16000; 
    final int SIZE_OF_RECORD_ARRAY = 1024; // 1024 ORIGINAL 
    final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1; 
    int i= 0; 
    boolean isPlaying = false; 

    // Local Bluetooth adapter 
    private BluetoothAdapter mBluetoothAdapter = null; 

    // Intent request codes 
    private static final int REQUEST_CONNECT_DEVICE = 1; 
    private static final int REQUEST_ENABLE_BT = 2; 

    private static final String HTC_MEDIA = "CSR8670-CNS 10001v4-STEREO"; 



    class MyThread extends Thread{ 
     private volatile boolean passThroughMode = true; 

     MyThread(){ 
      super(); 
     } 

     MyThread(boolean newPTV){ 
      this.passThroughMode = newPTV; 
     } 

     @Override 
     public void run(){ 
      short[] lin = new short[SIZE_OF_RECORD_ARRAY]; 
      int num = 0; 
      // record.startRecording(); 
      // track.play(); 
      while (passThroughMode) { 
      // while (!isInterrupted()) { 
       num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY); 
       for(i=0;i<lin.length;i++) 
        lin[i] *= WAV_SAMPLE_MULTIPLICATION_FACTOR; 
       track.write(lin, 0, num); 
      } 

      record.stop(); 
      track.stop(); 
//   record.release(); 
//   track.release(); 
     } 

     public void stopThread(){ 
      passThroughMode = false; 
     } 

     boolean getpassThroughMode(){ 
      return passThroughMode; 
     } 

     void setpassThroughMode(boolean value){ 
      this.passThroughMode = value; 
     } 

    } // class MyThread extends Thread{ CLOSED 

    MyThread newThread; 

    private void init() { 
     Log.d("MYLOG", "MainActivity init() called"); 
     int min = AudioRecord.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); 
     record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO, 
           AudioFormat.ENCODING_PCM_16BIT, min); 
     int maxJitter = AudioTrack.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); 
     track = new AudioTrack(AudioManager.MODE_IN_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO, 
           AudioFormat.ENCODING_PCM_16BIT, maxJitter, AudioTrack.MODE_STREAM); 
     am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); 
     am.setMode(AudioManager.MODE_IN_COMMUNICATION); 

    } 

    private void ensureDiscoverable() { 
     Log.d("MYLOG", "MainActivity ensure discoverable"); 
     if (mBluetoothAdapter.getScanMode() != 
      BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { 
      Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); 
      discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); 
      startActivity(discoverableIntent); 
     } 
    } 


    @SuppressLint({ "InlinedApi", "NewApi" }) 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION); 
     // init(); // -> Moved this to onResume(); 
     Log.d("MYLOG", "MainActivity onCreate() called"); 
     // Get local Bluetooth adapter 
     mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 

     // If the adapter is null, then Bluetooth is not supported 
     if (mBluetoothAdapter == null) { 
      Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); 
      finish(); 
      // return; 
     } 
     Log.d("MYLOG", "getProfileProxy() called"); 

    } 

    @Override 
    public void onStart() { 
     super.onStart(); 
     Log.e("MYLOG", "++ ON START ++"); 

     // If BT is not on, request that it be enabled. 
     // setupChat() will then be called during onActivityResult 
     if (!mBluetoothAdapter.isEnabled()) { 
      Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 
      startActivityForResult(enableIntent, REQUEST_ENABLE_BT); 
      // Otherwise, setup the chat session 
     } 

     // mConnectionClass = new ConnectionClass(); 
     mConnectionClass = new ConnectionClass(this); 
    } 

    @Override 
    protected void onResume(){ 
     super.onResume(); 
     // newThread.stopThread(); 
     Log.d("MYLOG", "onResume() called"); 
     init(); 
     newThread = new MyThread(true);   // ORIGINAL -> true 
     newThread.start(); 
    } 

    @Override 
    protected void onPause(){ 
     super.onPause(); 
     Log.d("MYLOG", "onPause() called"); 
     newThread.stopThread(); 
    } 


    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     getMenuInflater().inflate(R.menu.main, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     switch (item.getItemId()) { 
     case R.id.scan: 
      // Launch the DeviceListActivity to see devices and do scan 
      Intent serverIntent = new Intent(this, DeviceListActivity.class); 
      startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); 
      return true; 
     case R.id.discoverable: 
      // Ensure this device is discoverable by others 
      ensureDiscoverable(); 
      return true; 
     } 
     return false; 
    } 

    public void onActivityResult(int requestCode, int resultCode, Intent data) { 
     Log.d("MYLOG", "MainActivity onActivityResult " + resultCode); 
     switch (requestCode) { 
     case REQUEST_CONNECT_DEVICE: 
      // When DeviceListActivity returns with a device to connect 
      if (resultCode == Activity.RESULT_OK) { 
       // Get the device MAC address 
       String address = data.getExtras() 
            .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); 
       // Get the BLuetoothDevice object 
       // BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); 
       device = mBluetoothAdapter.getRemoteDevice(address); 
       // Attempt to connect to the device 
       // mChatService.connect(device); 
       Log.d("MYLOG", "ABOUT TO CONNECT"); 
       mConnectionClass.connect(device); 
      } 
      break; 
     case REQUEST_ENABLE_BT: 
      // When the request to enable Bluetooth returns 
      if (resultCode == Activity.RESULT_OK) { 
       // Bluetooth is now enabled, so set up a chat session 
       // setupChat(); 
       Toast.makeText(this, "BLUETOOTH ENABLED", Toast.LENGTH_LONG).show(); 
      } else { 
       // User did not enable Bluetooth or an error occured 
       Log.d("MYLOG", "BT not enabled"); 
       Toast.makeText(this, "BLUETOOTH NOT ENABLED", Toast.LENGTH_LONG).show(); 
       // finish(); 
      } 
     } 
    } 


    public void passStop(View view){ 
     Button playBtn = (Button) findViewById(R.id.playBtn); 
     Log.d("MYLOG", "in passStop()"); 

     if(!isPlaying){ 
      record.startRecording(); 
      track.play(); 
      isPlaying = true; 
      playBtn.setText("Pause"); 
     } 
     else{ 
      record.stop(); 
      track.pause(); 
      isPlaying=false; 
      playBtn.setText("Start"); 
     } 
    } 


    public void btnA2dpStart_onClick(View view){ 
//  onBluetoothConnected(); 
     new BluetoothA2DPRequester(this).request(this, mBluetoothAdapter); 
    } 

    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     newThread.stopThread(); 
     record.release(); 
     track.release(); 
//  unregisterReceiver(mReceiver); 
     // mBluetoothAdapter.disable(); 
     Log.d("MYLOG", "onDestroy() called"); 
    } 

    @Override 
    public void onA2DPProxyReceived (BluetoothA2dp proxy) { 
     Log.d("MYLOG", "MainActivity onA2DPProxyReceived(BluetoothA2dp proxy) called"); 
     Method connect = getConnectMethod(); 
     BluetoothDevice device = findBondedDeviceByName(mBluetoothAdapter, HTC_MEDIA); 

     //If either is null, just return. The errors have already been logged 
     if (connect == null || device == null) { 
      return; 
     } 

     try { 
      connect.setAccessible(true); 
      connect.invoke(proxy, device); 
     } catch (InvocationTargetException ex) { 
      Log.e("MYLOG", "Unable to invoke connect(BluetoothDevice) method on proxy. " + ex.toString()); 
     } catch (IllegalAccessException ex) { 
      Log.e("MYLOG", "Illegal Access! " + ex.toString()); 
     } 
    } 

    /** 
    * Wrapper around some reflection code to get the hidden 'connect()' method 
    * @return the connect(BluetoothDevice) method, or null if it could not be found 
    */ 
    @SuppressLint("NewApi") 
    private Method getConnectMethod() { 
     Log.d("MYLOG", "MainActivity getConnectMethod() called"); 
     try { 
      return BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class); 
     } catch (NoSuchMethodException ex) { 
      Log.e("MYLOG", "Unable to find connect(BluetoothDevice) method in BluetoothA2dp proxy."); 
      return null; 
     } 
    } 

    /** 
    * Search the set of bonded devices in the BluetoothAdapter for one that matches 
    * the given name 
    * @param adapter the BluetoothAdapter whose bonded devices should be queried 
    * @param name the name of the device to search for 
    * @return the BluetoothDevice by the given name (if found); null if it was not found 
    */ 
    private static BluetoothDevice findBondedDeviceByName (BluetoothAdapter adapter, String name) { 
     Log.d("MYLOG", "MainActivity findBondedDeviceByName(BluetoothAdapter adapter, String name) called"); 
     for (BluetoothDevice device : getBondedDevices(adapter)) { 
      if (name.matches(device.getName())) { 
       Log.v("MYLOG", String.format("Found device with name %s and address %s.", device.getName(), device.getAddress())); 
       return device; 
      } 
     } 
     Log.w("MYLOG", String.format("Unable to find device with name %s.", name)); 
     return null; 
    } 

    /** 
    * Safety wrapper around BluetoothAdapter#getBondedDevices() that is guaranteed 
    * to return a non-null result 
    * @param adapter the BluetoothAdapter whose bonded devices should be obtained 
    * @return the set of all bonded devices to the adapter; an empty set if there was an error 
    */ 
    private static Set<BluetoothDevice> getBondedDevices (BluetoothAdapter adapter) { 
     Log.d("MYLOG", "MainActivity getBondedDevices(BluetoothAdapter adapter) called"); 
     Set<BluetoothDevice> results = adapter.getBondedDevices(); 
     if (results == null) { 
      results = new HashSet<BluetoothDevice>(); 
     } 
     return results; 
    } 

} 

的BluetoothA2DPRequester類是在這裏:

@SuppressLint("NewApi") 
public class BluetoothA2DPRequester implements BluetoothProfile.ServiceListener { 
    private Callback mCallback; 

    /** 
    * Creates a new instance of an A2DP Proxy requester with the 
    * callback that should receive the proxy once it is acquired 
    * @param callback the callback that should receive the proxy 
    */ 
    public BluetoothA2DPRequester(Callback callback) { 
     Log.d("MYLOG", "BluetoothA2DPRequester Constructor; Action: mCallback = callback"); 
     mCallback = callback; 
    } 

    /** 
    * Start an asynchronous request to acquire the A2DP proxy. The callback 
    * will be notified when the proxy is acquired 
    * @param c the context used to obtain the proxy 
    * @param adapter the BluetoothAdapter that should receive the request for proxy 
    */ 
    @SuppressLint("NewApi") 
    public void request (Context c, BluetoothAdapter adapter) { 
     Log.d("MYLOG", "BluetoothA2DPRequester request(Context c, BluetoothAdapter adapter) called"); 
     adapter.getProfileProxy(c, this, BluetoothProfile.A2DP); 
    } 

    @Override 
    public void onServiceConnected(int i, BluetoothProfile bluetoothProfile) { 
     Log.d("MYLOG", "BluetoothA2DPRequester onServiceConnected(int i, BluetoothProfile bluetoothProfile) called"); 
     if (mCallback != null) { 
      mCallback.onA2DPProxyReceived((BluetoothA2dp) bluetoothProfile); 
     } 
    } 

    @Override 
    public void onServiceDisconnected(int i) { 
     //It's a one-off connection attempt; we don't care about the disconnection event. 
    } 

    public static interface Callback { 
     public void onA2DPProxyReceived (BluetoothA2dp proxy); 
    } 
} 

這是我從here獲得。

爲什麼服務連接泄漏錯誤發生,我該如何解決?它只會在onDestroy之後出現,而且我還沒有看到應用程序崩潰(因爲它)。它可以安全地忽略嗎?

我的猜測:

的BluetoothA2DPRequester類實現BluetoothProfile.ServiceListener當這個類的一個無名的臨時對象在這裏做

public void btnA2dpStart_onClick(View view){ 
//  onBluetoothConnected(); 
     new BluetoothA2DPRequester(this).request(this, mBluetoothAdapter); 
    } 

我假設服務監聽器被註冊到這個類和導致這個錯誤。這是怎麼回事?如果是這樣,我怎麼註銷它,因爲服務監聽器沒有名字?

回答

1

您需要的onDestroy()

BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP,sBluetoothA2dp); 
調用此