Skip to content

避免使用onActivityResult,提高代码内聚性,提高代码可读性,解决其业务逻辑代码日益壮大的问题.

License

Notifications You must be signed in to change notification settings

asiontang/YeAvoidOnResult

Repository files navigation

YeAvoidOnResult

避免使用onActivityResult,以提高代码可读性,解决其业务逻辑代码日益壮大的问题.

优雅的使用onActivityResult

需要和 BaseFragmentAvoidOnResult 保持一致

优化后方案:

使用onSaveInstanceState 和 onRestoreInstanceState 来恢复回调函数来实现此功能.

优势:

  1. 实现的方式有参考性(有别于网上已知的方案)
  2. 实现了"非匿名回调"里 调用 时,能解决上层 Activity 被 Destroy 后,返回时正常触发回调.(参考"局限性"一节的错误示范代码)
  3. 假如不考虑 "上层 Activity 被 Destroy 后能正常回调错误示范场景下"的情况,那么功能应该和网上已知方案是一致的.
  4. 支持大部分"简单情况下"的匿名函数里调用startActivityForResult,如常见控件的事件里等.

核心代码

    @Override
    protected void onRestoreInstanceState(final Bundle savedInstanceState)
    {
        super.onRestoreInstanceState(savedInstanceState);
        try
        {
            mActivityResultListenerClassArray = savedInstanceState.getBundle("mActivityResultListenerClassArray");
            if (mActivityResultListenerClassArray != null)
            {
                if (mActivityResultListenerArray == null)
                    mActivityResultListenerArray = new SparseArray<>();
                for (String requestCode : mActivityResultListenerClassArray.keySet())
                {
                    try
                    {
                        final String className = mActivityResultListenerClassArray.getString(requestCode);
                        final Constructor<?> constructor = Class.forName(className).getDeclaredConstructors()[0];
                        constructor.setAccessible(true);
                        final OnActivityResultListener c = (OnActivityResultListener) constructor.newInstance(this);
                        mActivityResultListenerArray.put(Integer.parseInt(requestCode), c);
                    }
                    catch (Throwable e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
        catch (Throwable e)
        {
            LogEx.e("BaseActivityAvoidOnResult", mActivityResultListenerClassArray, mActivityResultListenerArray, e);
        }
    }

局限性:(不支持的使用场景)

按照 @AnotherJackAvoidOnResult 的 issues 里提出来的建议,写了测试代码实际验证后发现的确存在问题.(新版本180912.03.01.004部分解决了此问题)

创建listener的代码是在点击事件中,在OnClickListener中,是不是这个构造器的参数类型就不是外部Activity而是OnClickListener了

不支持的使用场景 - 示范代码1:

匿名类里带变量的情况.简单的通过反射实例化的话可能会导致数据状态不一致.所以新版本180912.03.01.004也不支持此类情况.

        this.btnLogin.setOnClickListener(new OnClickListener()
        {
            int testCode = 0;

            @Override
            public void onClick(final View v)
            {
                startActivityForResult(new Intent(getApplicationContext(), ServerSettingActivity.class), new OnActivityResultListener()
                {
                    @Override
                    public void onActivityResult(final int resultCode, final Intent data)
                    {
                        testCode++;
                        ToastEx.makeTextAndShowLong("testCode=" + testCode + " resultCode=" + resultCode);
                    }
                });
            }
        });
不支持的使用场景 - 示范代码2:

新版本180912.03.01.004已经可以支持此类情况.

        this.btnLogin.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(final View v)
            {
                startActivityForResult(new Intent(getApplicationContext(), ServerSettingActivity.class), new OnActivityResultListener()
                {
                    @Override
                    public void onActivityResult(final int resultCode, final Intent data)
                    {
                        ToastEx.makeTextAndShowLong(" resultCode=" + resultCode);
                    }
                });
            }
        });
错误提示:
System.err: java.lang.IllegalArgumentException: argument 1 should have type LoginActivity$5, got LoginActivity
        at java.lang.reflect.Constructor.constructNative(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at cn.asiontang.BaseActivityAvoidOnResult.onRestoreInstanceState(BaseActivityAvoidOnResult.java:96)
原因初解:

new OnActivityResultListener 里调用了 new OnClickListener 的变量,导致实际生成的OnActivityResultListener匿名类名字为 LoginActivity$5$1 ,因此其默认的构造函数具有唯一的一个参数类型必须为LoginActivity$5.

局限性小结:

必须保证 OnActivityResultListener的匿名回调类new生成 是在 其子类直属 范围内 即可. 不能在匿名函数里再 new OnActivityResultListener 出来调用.

Update:

新版本180912.03.01.004解决了此问题的部分情况.

使用代码

正确的使用方式 - 代码示范0:

新版本180912.03.01.004支持大部分"简单情况下"的匿名函数里调用startActivityForResult,如常见控件的事件里等.

        this.btnLogin.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(final View v)
            {
                startActivityForResult(new Intent(getApplicationContext(), ServerSettingActivity.class), new OnActivityResultListener()
                {
                    @Override
                    public void onActivityResult(final int resultCode, final Intent data)
                    {
                        ToastEx.makeTextAndShowLong(" resultCode=" + resultCode);
                    }
                });
            }
        });

正确的使用方式 - 代码示范1:

public class LoginActivity extends BaseActivity implements OnClickListener
{
    @Override
    public void onCreate(final android.os.Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.login);

        //other code

        this.btnLogin.setOnClickListener(this);
    }

    @Override
    public void onClick(final View v)
    {
        switch (v.getId())
        {
            case R.id.btnLogin:
                startActivityForResult(new Intent(getApplicationContext(), ServerSettingActivity.class), new OnActivityResultListener()
                {
                    @Override
                    public void onActivityResult(final int resultCode, final Intent data)
                    {
                        ToastEx.makeTextAndShowLong(" resultCode=" + resultCode);
                    }
                });
                break;
        }
    }
}

正确的使用方式 - 代码示范2:

public class LoginActivity extends BaseActivity
{
    @Override
    public void onCreate(final android.os.Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.login);

        //other code

        this.btnLogin.setOnClickListener(setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(final View v)
            {
            	onLoginClick();
            }
        });
    }

    public void onLoginClick()
    {
        startActivityForResult(new Intent(getApplicationContext(), ServerSettingActivity.class), new OnActivityResultListener()
        {
            @Override
            public void onActivityResult(final int resultCode, final Intent data)
            {
                ToastEx.makeTextAndShowLong(" resultCode=" + resultCode);
            }
        });
    }
}

正确的使用方式 - 代码示范3:

public class LoginActivity extends BaseActivity implements OnClickListener
{
    @Override
    public void onCreate(final android.os.Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.login);

        //other code
        
        final OnActivityResultListener mListener = new OnActivityResultListener()
        {
            @Override
            public void onActivityResult(final int resultCode, final Intent data)
            {
                ToastEx.makeTextAndShowLong(" resultCode=" + resultCode);
            }
        };
        this.btnLogin.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(final View v)
            {
                startActivityForResult(new Intent(getApplicationContext(), ServerSettingActivity.class), mListener);
            }
        });
    }
}

网上的方案

当A被销毁后,从B返回时,A会被重建,此时回调函数无法自动创建导致无效!

遇到activity进入后台被系统回收的情况,就没办法接收到回调了,这点可以开启开发者选项->不保留活动 来验证

参考资料:

  1. java - onDestroy() while "waiting" for onActivityResult() - Stack Overflow
  2. Activity被回收后Callback不会回调 · Issue #4 · AnotherJack/AvoidOnResult
  3. EasyAndroid基础集成组件库之:EasyActivityResult 拒绝臃肿的onActivityResult代码 - 掘金

@author AsionTang
@version 180912.02.01.003

About

避免使用onActivityResult,提高代码内聚性,提高代码可读性,解决其业务逻辑代码日益壮大的问题.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages