Posts Tagged ‘Android’

Android 数据通信:从基本的Activity、Fragment、Service数据传递到EventBus的使用

in 垃圾文章

本文主要内容:首先介绍 Android 自带的 Activity 互相传递数据、Activity 与 Fragment 互相传递数据、Activity 与 Service 互相传递数据。随后分析它们的使用场景与优缺点,引出 EventBus,最后介绍 EventBus 的基本使用。
注:本文 Demo 皆为 kotlin 编写,有 Java 基础的读者应该能大致理解,请放心阅读。
注2:本文属于垃圾文章补完计划

Android 自带的数据通信机制

Activity 与 Activity 互相传递数据

我们将从 AActivity 启动 BActivity,并通过 intent 传送数据。
AActivity 的 onCreate 方法内:

            into_b.setOnClickListener {
            val intent = Intent(baseContext, BActivity::class.java)
            //传递基本类型
            intent.putExtra("att1", "普通的字符串")
            //传递序列化对象
            val person = Person("xloger", 18)
            intent.putExtra("att2", person)

            startActivity(intent)
        }

BActivity 的 onCreate 方法内:

        val sendMsg = intent.getStringExtra("att1")
        val person = intent.getSerializableExtra("att2") as? Person
        Log.d("XExample", "收到的消息:$sendMsg,收到的对象:$person")

输出结果:

D/XExample: 收到的消息:普通的字符串,收到的对象:Person(name=xloger, age=18)

注意,虽然 intent 看似有传 extra 和传 bundle 两种方式,但是 extra 只是 bundle 的封装。需要传递对象时需该类实现 Serializable 接口。
而当需要接收 BActivity 的返回数据时,需将原先的startActivity(intent)改为startActivityForResult(intent,233),其中整型233为 requestCode,作用是区分不同请求。
而 BActivity 在销毁前通过以下代码即可传递数据给 AActivity:

        val resultIntent = Intent()
        resultIntent.putExtra("resultMsg", "返回的内容")
        setResult(124, resultIntent)

然后在 AActivity 类中实现以下方法:

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 233 && resultCode == 124) {
            Log.d("XExample", "返回的内容为:${data?.getStringExtra("resultMsg")}")
        }
    }

当进入 BActivity 后返回 AActivity,输出结果:

D/XExample: 返回的内容为:返回的内容

注意,requsetCode 与 resultCode 目的是为了区分不同的请求。而实际开发中,这些数应该统一放入常量池中,防止数据过多时不好区分。

(更多…)

ViewPager 的点击监听与存在的误触问题

in 在路上

ViewPager 是不自带 onItemClickListener 之类的方法的,解决方案也很简单,就是在 PagerAdapter 的 instantiateItem 方法里为每个页面加上监听。这是业界通常的做法,但是为毛没一个人提到这样会产生误触问题呢Orz……虽然解决误触也不复杂啦,但是我还是纳闷为啥我之前搜都没有相关资料呢……所以我还是写下这篇帮助下会被此困扰的人吧。

ViewPager 的点击事件监听

public class StartViewPagerAdapter extends PagerAdapter {
    private List mListViews;
    private View.OnClickListener clickListener;

    public StartViewPagerAdapter(List mListViews,View.OnClickListener clickListener) {
        this.mListViews = mListViews;
        this.clickListener=clickListener;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {  
        container.addView(mListViews.get(position), 0);
        mListViews.get(position).setOnClickListener(clickListener);
        //TODO 后面解决误触的代码
        return mListViews.get(position);
    }
}

然后创建的时候把 clickListener 传进来就行,没啥好说的。
(更多…)

Android序列化中由于修改类的内容造成InvalidClassException的解决方案

in 在路上

好多年没写博文了,我果然没救了。不过这篇文章定义应该是符合并不是互联网里满大街的知识点的要求吧。

问题回顾

在我的Exlink2.0版本升级到2.1中,之前保存的规则信息全部被清空了,而我的规则信息是通过对象序列化存储的。经初步排查,报错信息为

java.io.InvalidClassException: com.xloger.exlink.app.b.c; Incompatible class (SUID): com.xloger.exlink.app.b.c: static final long serialVersionUID =86222585354990243L; but expected com.xloger.exlink.app.b.c: static final long serialVersionUID =-4642583507468338732L;

解决这个报错的方案网上有很多,但是尝试后并没有解决我的问题,因此写下这篇博文。

序列化与serialVersionUID

序列化就是将一个对象转换为一个字节序列(目的是能存储和传输对象)。而上面的InvalidClassException恰好就是在反序列化(将一个字节序列转换回一个对象)时会产生的。

而这个InvalidClassException的原因是这样的:在一个对象(比如继承自Rule类)与字节序列的相互转换中,必然需要一个标记来证明它是Rule类的对象,否则谁知道你是Rule类还是String类还是什么,那没法反序列化了。因此,Java规定了一个属性serialVersionUID来区分它们,这个属性是可以在你需要序列化的类里申明的,比如这样:

private static final long serialVersionUID=10086L;

设定为private是为了不被子类继承。当然了,大部分人会说我以前没写过serialVersionUID啊,都是继承了Serializable接口就没了啊,是的,当没有申明serialVersionUID的时候,JVM会自动地根据包名、类名、继承关系、非私有的方法和属性,以及参数、返回值等诸多因子计算出一个值。比如我的Exlink在2.0版本之前,它给我计算的这个值为86222585354990243L,而在2.1版本中我对Rule类做了一些修改,导致该serialVersionUID变成了-4642583507468338732L。因此虽然我做的改动并不会真的影响到反序列化,但是JVM依旧会认为这有问题,因此抛出了InvalidClassException。

Ok,解决方案很简单,为了兼容以前的版本,我仅仅需要在Rule类里把serialVersionUID定义为86222585354990243L,这样JVM在反序列化的时候依旧会把最新版的Rule和旧的Rule当成一个来处理了。

也许有人会纳闷一些兼容性的问题,首先分成向上兼容、向下兼容两种。假如我们的新版增加了一个属性比如age,向上兼容就是旧的客户端读取新的数据,这里JVM会自动把那些它不认识的数据忽略掉(比如age),所以只要不改动旧属性,不用担心向上兼容的问题。向下兼容是新的客户端读取旧的数据,对于新的属性都会为Java里该类型的默认值,比如null、0、false这些。哪怕你定义你的成员变量private int age=12,这里的值依旧是0。那想做向下兼容应该怎么处理呢,就是在读取数据之后进行检测,判断是旧版本后手动初始化,比如可以维护一个当前类的版本的变量来判断。或者Android可以直接读取程序的versionCode来作为判断变量。

(更多…)

© 2019 一隅 - Powered by Wordpress / Theme: Tabinikki