如何解决" Realm从错误的线程访问。 Realm对象只能在它们创建的线程上访问"

时间:2017-03-31 13:30:11

标签: android android-widget realm android-appwidget realm-mobile-platform

我正在尝试为我的应用制作应用小部件,并将收藏的商品列表显示到应用小部件中。但是,它显示错误:

  

" Realm从错误的线程访问。领域对象只能是   在他们创建的主题上访问。"

FATAL EXCEPTION: Binder:3017_3
                                                                               Process: com.santossingh.capstoneproject, PID: 3017
                                                                               java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
                                                                                   at io.realm.BaseRealm.checkIfValid(BaseRealm.java:353)
                                                                                   at io.realm.RealmResults.isLoaded(RealmResults.java:88)
                                                                                   at io.realm.OrderedRealmCollectionImpl.size(OrderedRealmCollectionImpl.java:303)
                                                                                   at io.realm.RealmResults.size(RealmResults.java:53)
                                                                                   at com.santossingh.capstoneproject.Widget.ListProvider.getCount(ListProvider.java:57)
                                                                                   at android.widget.RemoteViewsService$RemoteViewsFactoryAdapter.getCount(RemoteViewsService.java:154)
                                                                                   at com.android.internal.widget.IRemoteViewsFactory$Stub.onTransact(IRemoteViewsFactory.java:75)
                                                                                   at android.os.Binder.execTransact(Binder.java:565)
03-31 20:32:01.058 1298-1458/? E/SurfaceFlinger: ro.sf.lcd_density must be defined as a build property

如果您了解它,请给我一个关于访问其他活动数据的明确示例,或者如何调用创建域数据库的特定线程。

1- ConfigActivity

public class ConfigActivity extends AppCompatActivity {

    private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    Realm realm;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_config);
        Realm.init(this);
        RealmConfiguration config = new RealmConfiguration.Builder()
                .deleteRealmIfMigrationNeeded()
                .build();
        realm=Realm.getInstance(config);
        assignAppWidgetId();
        startWidget();
    }

    /**
     * Widget configuration activity,always receives appwidget Id appWidget Id =
     * unique id that identifies your widget analogy : same as setting view id
     * via @+id/viewname on layout but appwidget id is assigned by the system
     * itself
     */
    private void assignAppWidgetId() {
        Bundle extras = getIntent().getExtras();
        if (extras != null)
            appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    /**
     * This method right now displays the widget and starts a Service to fetch
     * remote data from Server
     */
    private void startWidget() {

        // this intent is essential to show the widget
        // if this intent is not included,you can't show
        // widget on homescreen
        Intent intent = new Intent();
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        setResult(Activity.RESULT_OK, intent);

        // start your service
        // to fetch data from web
        Intent serviceIntent = new Intent(this, ListProvider.class);
        serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        startService(serviceIntent);

        // finish this activity
        this.finish();

    }

2- ListProvider类

public class ListProvider implements RemoteViewsService.RemoteViewsFactory {
    private MyHandlerThread mHandlerThread=new MyHandlerThread();
    private Context context = null;
    private int appWidgetId;
    RealmResults<FavoriteBooks> booksList;
    RealmConfiguration config;

    public ListProvider(Context context, Intent intent) {
        this.context = context;
        appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);

        getDataFromRealm(context);
    }

    @Override
    public void onCreate() {

    }

    @Override
    public void onDataSetChanged() {
        getDataFromRealm(context);
    }

    @Override
    public void onDestroy() {
    }

    @Override
    public int getCount() {
        return booksList.size();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }


    @Override
    public RemoteViews getViewAt(final int position) {
        final Lock lock = new Lock();
        final RemoteViews[] result = {null};
        mHandlerThread.getHandler().post(new Runnable() {
            @Override
            public void run() {
                // You can safely access results here.
                result[0] = new RemoteViews(context.getPackageName(), R.layout.widget_item);
                if (booksList!=null) {
                    FavoriteBooks book = booksList.get(position);
                    result[0].setTextViewText(R.id.widgetText, book.getTitle());
                }
                lock.unlock();

            }
        });
        try {
            lock.lock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result[0];
    }

    @Override
    public RemoteViews getLoadingView() {
        return null;
    }

    @Override
    public int getViewTypeCount() {
        return 0;
    }

    //------------
    public class Lock {
        private boolean isLocked;

        public synchronized void lock() throws InterruptedException {
            isLocked = true;
            while (isLocked) {
                wait();
            }
        }

        public synchronized void unlock() {
            isLocked = false;
            notify();
        }
    }

    public class MyHandlerThread extends HandlerThread {
        private Handler mHandler;

        public MyHandlerThread() {
            super("MY_HANDLER_THREAD");
            start();
            mHandler = new Handler(getLooper());
        }

        public Handler getHandler() {
            return mHandler;
        }
    }

    private void getDataFromRealm(final Context context) {
        final Lock lock = new Lock();
        mHandlerThread.getHandler().post(new Runnable() {
            @Override
            public void run() {
                Realm.init(context);
                config = new RealmConfiguration.Builder()
                        .name("favorite1.realm")
                        .schemaVersion(1)
                        .deleteRealmIfMigrationNeeded()
                        .build();
                Realm realm = Realm.getInstance(config);
                booksList = realm.where(FavoriteBooks.class).findAll();
                lock.unlock();
            }
        });
        try {
            lock.lock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

3- WidgetService类

public class WidgetService extends RemoteViewsService {
/*
* So pretty simple just defining the Adapter of the listview
* here Adapter is ListProvider
* */

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        int appWidgetId = intent.getIntExtra(
                AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);

        // call and return listprovider class
        return (new ListProvider(this.getApplicationContext(), intent));
    }

}

4- WidgetProvider类

public class WidgetProvider extends AppWidgetProvider {

    public final static String DATA_FETCHED="com.santossingh.capstoneproject.DATA_FETCHED";

    @Override
    public void onUpdate(Context context, AppWidgetManager
            appWidgetManager, int[] appWidgetIds) {

        final int N = appWidgetIds.length;
        for (int i = 0; i < N; ++i) {

            RemoteViews remoteViews = updateAppWidget(context,
                    appWidgetIds[i]);
            appWidgetManager.updateAppWidget(appWidgetIds[i],
                    remoteViews);
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

    private RemoteViews updateAppWidget(Context context, int appWidgetId) {

        //which layout to show on widget
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);

        //RemoteViews Service needed to provide adapter for ListView
        Intent svcIntent = new Intent(context, WidgetService.class);
        //passing app widget id to that RemoteViews Service
        svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        //setting a unique Uri to the intent
        //don't know its purpose to me right now
        svcIntent.setData(Uri.parse(
                svcIntent.toUri(Intent.URI_INTENT_SCHEME)));
        //setting adapter to gridView of the widget
        remoteViews.setRemoteAdapter(appWidgetId, R.id.gridViewWidget,
                svcIntent);
        //setting an empty view in case of no data
        remoteViews.setEmptyView(R.id.gridViewWidget, R.id.emptyTextViewWidget);
        return remoteViews;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        if (intent.getAction().equals(DATA_FETCHED)) {
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            RemoteViews remoteViews = updateAppWidget(context, appWidgetId);
            appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
        }
    }
}

5- RemoteFetchService类

public class RemoteFetchService extends Service {

    private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;


    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    /**
     * Retrieve appwidget id from intent it is needed to update widget later
     * initialize our AQuery class
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID))
            appWidgetId = intent.getIntExtra(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);

        populateWidget();
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * Method which sends broadcast to WidgetProvider
     * so that widget is notified to do necessary action
     * and here action == WidgetProvider.DATA_FETCHED
     */
    private void populateWidget() {

        Intent widgetUpdateIntent = new Intent();
        widgetUpdateIntent.setAction(WidgetProvider.DATA_FETCHED);
        widgetUpdateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                appWidgetId);
        sendBroadcast(widgetUpdateIntent);

        this.stopSelf();
    }

6- FavoriteBooks类扩展了RealmObject

public class FavoriteBooks extends RealmObject {

    @PrimaryKey
    private String id;
    private String title;
    private String author;
    private String buyLink;
    private String image;
    private String price;
    private String publishedDate;
    private String reviewLink;
    private String description;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getBuyLink() {
        return buyLink;
    }

    public void setBuyLink(String buyLink) {
        this.buyLink = buyLink;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public String getPublishedDate() {
        return publishedDate;
    }

    public void setPublishedDate(String publishedDate) {
        this.publishedDate = publishedDate;
    }

    public String getReviewLink() {
        return reviewLink;
    }

    public void setReviewLink(String reviewLink) {
        this.reviewLink = reviewLink;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }
}

所以请帮助我......如果你能纠正我的ListProvider.class,那将是很好的,

@Override
    public int getCount() {
        return booksList.size();
    }

1 个答案:

答案 0 :(得分:0)

简明回答:Realm结果是延迟加载的,所以我说你不需要在另一个线程上执行查询。为RemoteView(或ListProvider)创建一个领域,然后使用它来正常查询。 Realm对象只会在访问时加载数据。