0

Я пытаюсь передать актив, следуя андроид обучения разработчиков, который говорит, чтобы использовать этот код:Как перенести продукт Android без блокировки потока пользовательского интерфейса?

@Override 
public void onDataChanged(DataEventBuffer dataEvents) { 
    for (DataEvent event : dataEvents) { 
    if (event.getType() == DataEvent.TYPE_CHANGED && 
     event.getDataItem().getUri().getPath().equals("/image")) { 
     DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem()); 
     Asset profileAsset = dataMapItem.getDataMap().getAsset("profileImage"); 
     Bitmap bitmap = loadBitmapFromAsset(profileAsset); 
     // Do something with the bitmap 
    } 
    } 
} 

public Bitmap loadBitmapFromAsset(Asset asset) { 
    if (asset == null) { 
     throw new IllegalArgumentException("Asset must be non-null"); 
    } 
    ConnectionResult result = 
      mGoogleApiClient.blockingConnect(TIMEOUT_MS, TimeUnit.MILLISECONDS); 
    if (!result.isSuccess()) { 
     return null; 
    } 
    // convert asset into a file descriptor and block until it's ready 
    InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
      mGoogleApiClient, asset).await().getInputStream(); 
      mGoogleApiClient.disconnect(); 

    if (assetInputStream == null) { 
     Log.w(TAG, "Requested an unknown Asset."); 
     return null; 
    } 
    // decode the stream into a bitmap 
    return BitmapFactory.decodeStream(assetInputStream); 
} 

Так что я сделал то же самое в примерно таким же образом:

// Build a new GoogleApiClient for the Wearable API 
    googleClient = new GoogleApiClient.Builder(this) 
      .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { 
       @Override 
       public void onConnected(Bundle bundle) { 
        Wearable.DataApi.addListener(googleClient, onDataChangedListener); 
       } 

       @Override 
       public void onConnectionSuspended(int i) { 

       } 
      }) 
      .addApi(Wearable.API) 
      .build(); 
    googleClient.connect(); 

и в моем onDatachanged метод у меня есть:

public DataApi.DataListener onDataChangedListener = new DataApi.DataListener() { 
    @Override 
    public void onDataChanged(DataEventBuffer dataEvents) { 
     Log.d(TAG, "Data changed: " + dataEvents); 

     for (DataEvent event : dataEvents) { 
      Log.d(TAG, "Data received: " + event.getDataItem().getUri()); 

      if (event.getType() == DataEvent.TYPE_CHANGED && 
       event.getDataItem().getUri().getPath().equals("/audio")) { 
       DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem()); 
       Asset audioAsset = dataMapItem.getDataMap().getAsset("audioAsset"); 
       audioBytes = loadBytesFromAsset(audioAsset); 
      } 

      // Set play button enabled 
      handler.post(onNewAudio()); 
     } 
    } 
} 

с моим loadBytesFromAsset() метод:

public byte[] loadBytesFromAsset(Asset asset) { 
    if (asset == null) { 
     throw new IllegalArgumentException("Asset must be non-null"); 
    } 

    result = googleClient.blockingConnect(3000, TimeUnit.MILLISECONDS); 
    if(!result.isSuccess()){ 
     return null; 
    } 

    // Convert asset into a file descriptor and block until it's ready 
    InputStream assetInputStream = Wearable.DataApi.getFdForAsset(googleClient, asset).await().getInputStream(); 
    googleClient.disconnect(); 

    if (assetInputStream == null) { 
     Log.w(TAG, "Requested an unknown Asset."); 
     return null; 
    } 
    // Decode the stream into a byte[] 
    return getBytesFromInputStream(assetInputStream); 
} 

Это похоже на то, что предлагает тренинг для разработчиков Android, но когда я запускаю его, метод loadBytesFromAsset() выходит из строя с исключением, что я не могу вызвать blockingConnect() в потоке пользовательского интерфейса. Кто-нибудь знает, как это решить? Как я должен слушать и извлекать активы? Заранее спасибо.

+1

Вы можете вставлять это в 'AsyncTask' или делать какие-либо другие асинхронные способы. –

ответ

2

Хорошо, у меня это работает (вроде), все еще возникают проблемы с вызовом onDataChanged, но проблема с потоком пользовательского интерфейса и вызовом blocking.connect была решена повторным выполнением кода, подобного этому сообщению here. То, как они сделали это, сделав класс реализации, GoogleApiClient.ConnectionCallbacks и интерфейсы DataApi.DataListener GoogleApiClient.OnConnectionFailedListener, например, так:

public class MainActivity extends Activity implements 
     DataApi.DataListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{ 
    private TextView mTextView; 
    private static final long CONNECTION_TIME_OUT_MS = 100; 
    private static final String ON_MESSAGE = "On!"; 
    private static final String OFF_MESSAGE = "Off!"; 
    private static final String TAG = "Moto360DisplayControl"; 
    private GoogleApiClient client; 
    private String nodeId; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     initApi(); 
    } 

    private void initApi() { 
     client = getGoogleApiClient(this); 
     retrieveDeviceNode(); 
    } 

    private GoogleApiClient getGoogleApiClient(Context context) { 
     return new GoogleApiClient.Builder(context) 
      .addApi(Wearable.API) 
      .addConnectionCallbacks(this) 
      .addOnConnectionFailedListener(this) 
      .build(); 
    } 

    private void retrieveDeviceNode() { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS); 
       NodeApi.GetConnectedNodesResult result = 
        Wearable.NodeApi.getConnectedNodes(client).await(); 
       List<Node> nodes = result.getNodes(); 
       if (nodes.size() > 0) { 
        nodeId = nodes.get(0).getId(); 
       } 
       client.disconnect(); 
      } 
     }).start(); 
    } 

    @Override 
    protected void onStart() { 
     super.onStart(); 
     client.connect(); 
    } 

    @Override 
    public void onConnected(Bundle connectionHint) { 
     Wearable.DataApi.addListener(client, this); 
     Toast.makeText(this, "AddedListener!", Toast.LENGTH_LONG).show(); 
    } 

    @Override 
    public void onConnectionSuspended(int num) { 
     Toast.makeText(this, "ConnectionSuspended", Toast.LENGTH_LONG).show(); 
    } 

    @Override 
    public void onConnectionFailed(ConnectionResult res) { 
     Toast.makeText(this, "ConnectionFailed", Toast.LENGTH_LONG).show(); 
    } 

    @Override 
    protected void onStop() { 
     Wearable.DataApi.removeListener(client, this); 
     client.disconnect(); 
     super.onStop(); 
    } 

    @Override 
    public void onDataChanged(DataEventBuffer dataEvents) { 
     Toast.makeText(this, "DataChanged!", Toast.LENGTH_LONG).show(); 
     for (DataEvent event : dataEvents) { 
      if (event.getType() == DataEvent.TYPE_CHANGED && event.getDataItem().getUri().getPath().equals("/image")) { 
       DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem()); 
       Asset profileAsset = dataMapItem.getDataMap().getAsset("profileImage"); 
       Bitmap bitmap = loadBitmapFromAsset(profileAsset); 
       // Do something with bitmap 
       Toast.makeText(this, "DataChanged!", Toast.LENGTH_LONG).show(); 
      } 
     } 
    } 

    public Bitmap loadBitmapFromAsset(Asset asset) { 
     if (asset == null) { 
      throw new IllegalArgumentException("Asset must be non-null"); 
     } 

     ConnectionResult result = client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS); 
     if (!result.isSuccess()) { 
      return null; 
     } 

     // Convert asset into a file descriptor and block until it's ready 
     InputStream assetInputStream = Wearable.DataApi.getFdForAsset(client, asset).await().getInputStream(); 
     client.disconnect(); 

     if (assetInputStream == null) { 
      Log.w(TAG, "Requested an unknown Asset."); 
      return null; 
     } 

     // Decode the stream into a bitmap 
     return BitmapFactory.decodeStream(assetInputStream); 
    } 
} 

Используя этот метод, проблема была решена, я до сих пор работая над попыткой решить проблему onDataChanged, не вызываемой, которую я задал here.

1

(Отказ от ответственности: Я никогда не тестировал этот ответ, но это только мне чтение через API)

Пожалуйста, смотрите это - https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient

Вы можете видеть, что наряду connectBlocking вы также connect. И в документации указано

Соединяет клиента с сервисами Google Play. Этот метод немедленно возвращает и подключается к службе в фоновом режиме. Если соединение выполнено успешно, вызывается onConnected (Bundle) и ставится в очередь . При сбое вызывается onConnectionFailed (ConnectionResult) .

Так что вам нужно сделать, это позвонить registerConnectionCallbacks и передать его ConnectionCallbacks, который реализует onConnected. Эти обратные вызовы будут выполняться в потоке пользовательского интерфейса (точно так же, как и текущие обратные вызовы). Кроме того, вы также можете сделать то же самое с isConnectionFailedListenerRegistered, который вызывается при сбое соединения. Это на самом деле то, что вы уже делаете в первом сегменте своего кода, просто там, где вы устанавливаете слушателя в построителе.

Для этого потребуется некоторая смена кода, но я не думаю, что это слишком серьезно.

+0

О, и не забудьте позвонить \t unregisterConnectionCallbacks, когда вы являетесь обратным вызовом, завершает свою работу – Ginandi

0

Учитывая структуру кода, которая у вас есть, мне кажется, что вы регистрируете свой onDataChangedListener в обратном вызове onConnected() (это правильное место).В вашем обратном вызове onDataChangedListener # onDataChanged() (который вызывается в основном потоке) вы вызываете loadBytesFromAsset(). В этом методе вам не нужно снова подключать клиент google api; он должен быть уже подключен в этой точке, поэтому нет необходимости вызывать метод блокировки соединения. Рекомендуется проверить, что вы подключаетесь (apiClient.isConnected()), а затем выполняйте то, что вы хотели бы сделать.

Также не нужно отключать клиент API до того, как покинуть приложение (на самом деле, лучше не делать этого), если вы действительно не уверены, что не хотите делать что-либо еще в своем приложении, которое вам понадобится связь; лучше просто вызвать disconnect в onStop() вашей активности (и установить соединение в onStart()).

При этом, если какой-либо процесс, который вам нужно выполнить в этих обратных вызовах (которые находятся в основном потоке), является длительным процессом, тогда вам нужно будет создать отдельный поток (скажем, используя AsyncTask, IntentService или что-то еще такого рода) и обрабатывать длительный процесс там.