美文网首页Android开发xamarin开发技术Android开发经验谈
解决Xamarin.Android绑定第三方库时类型丢失的问题(

解决Xamarin.Android绑定第三方库时类型丢失的问题(

作者: 临岁之寒 | 来源:发表于2019-02-09 14:00 被阅读9次

    在Crasheye的SDK时,我再一次遇到了绑定问题,之前的问题请看解决Xamarin.Android绑定第三方库时类型丢失的问题(一),这一次出现的问题更多,也更棘手,其中几条查阅官方文档也没有发现解决方案。
    问题是这样的:


    下面我们一条一条来看,第一条问题报告如下:
    CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Base.BaseDao.cs(29,29): Error CS0111: Type 'BaseDao' already defines a member called 'Delete' with the same parameter types (CS0111) (CrashEyeTestPlus)

    查看问题源,发现文件中生成了两个一模一样的方法,连参数也是一样的:

    
        // Metadata.xml XPath method reference: path="/api/package[@name='com.xsj.crasheye.dao.base']/class[@name='BaseDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='ID']]"
            [Register ("delete", "(Ljava/io/Serializable;)I", "GetDelete_Ljava_io_Serializable_Handler")]
            public virtual unsafe int Delete (global::Java.Lang.Object id)
            {......}
    
              ........
    
            // Metadata.xml XPath method reference: path="/api/package[@name='com.xsj.crasheye.dao.base']/class[@name='BaseDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='T']]"
            [Register ("delete", "(Ljava/lang/Object;)I", "GetDelete_Ljava_lang_Object_Handler")]
            public virtual unsafe int Delete (global::Java.Lang.Object entity)
            {.......}
    
    

    为了搞清楚怎么回事,我还是和上次一样,在AS里查看一下原生的接口是什么样子:

    public abstract class BaseDao<T, ID extends Serializable> implements YoDao<T, ID> {
        .....
        public int delete(ID id) {
            return this.deleteByFields(this.whereClauseByPK(), this.whereArgsByPK(id));
        }
    
        public int delete(T entity) {
            int count = 0;
            if (entity != null) {
                ID id = this.getPK(entity);
                if (id != null) {
                    count = this.delete(id);
                }
            }
    
            return count;
        }
        .....
    }
    

    可见T和ID都是泛型,C#对Java的泛型支持并不太好,所以Xamarin在封装的时候,把T和ID都当成了Object类型对待,因此导致两个方法封装成了一模一样的外观。为了令两个方法能相互区别,我将delete(ID id) 的参数类型修改为Serializable类型(ID本来就是一个Serializable)。在Metedata.xml文件中加入如下代码:
    <attr path="/api/package[@name='com.xsj.crasheye.dao.base']/class[@name='BaseDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='ID']]/parameter[1]" name="managedType">Java.IO.ISerializable</attr>

    第二个问题是CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Base.IYoDao.cs(7,7): Error CS0111: Type 'IYoDao' already defines a member called 'Delete' with the same parameter types (CS0111) (CrashEyeTestPlus)

    这个问题的原因和上面是一样的,BaseDao的delete方法就是对IYoDao接口的实现,

    public interface YoDao<T, ID extends Serializable> {
          ......
        int delete(ID var1);
        int delete(T var1);
          .......
    }
    

    所以我们同样把delete(ID var1)的参数类型修改过来即可。在Metedata.xml文件中加入如下代码:

    <attr path="/api/package[@name='com.xsj.crasheye.dao.base']/interface[@name='YoDao']/method[@name='delete' and count(parameter)=1 and parameter[1][@type='ID']]/parameter[1]" name="managedType">Java.IO.ISerializable</attr>

    第三个问题是 CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Impl.SessionDaoImpl.cs(23,23): Error CS0534: 'SessionDaoImpl' does not implement inherited abstract member 'BaseDao.SetPK(Object, Object)' (CS0534) (CrashEyeTestPlus)
    查看一下生成的SessionDaoImpl文件,能够看到

        public partial class SessionDaoImpl : global::Com.Xsj.Crasheye.Dao.Base.BaseDao, global::Com.Xsj.Crasheye.Dao.ISessionDao {
    
          ......
        public virtual unsafe global::Com.Xsj.Crasheye.Session.Session SetPK (global::Com.Xsj.Crasheye.Session.Session entity, global::Java.Lang.Long id){
          ......
         }
    }
    

    其中Com.Xsj.Crasheye.Dao.ISessionDao的原生实现是这样的:

    public interface SessionDao extends YoDao<Session, Long> {
    }
    

    因此可知,在SessionDaoImpl下T和ID已经被声明为Session和Long,但Xamarin并不知道,他仍要求SessionDaoImpl要有一个BaseDao.SetPK(Object, Object)的实现,但BaseDao并不知道T和ID的具体类型是什么,所以我们只能修改SessionDaoImpl中的SetPK方法的参数类型以匹配BaseDao.SetPK(Object, Object)。在Metedata.xml文件中加入如下代码:

      <attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]/parameter[1]" name="managedType">Java.Lang.Object</attr>
        <attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]/parameter[2]" name="managedType">Java.Lang.Object</attr>
    

    但问题还没有结束,CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Impl.SessionDaoImpl.cs(23,23): Error CS0534: 'SessionDaoImpl' does not implement inherited abstract member 'BaseDao.SetPK(Object, Object)' (CS0534) (CrashEyeTestPlus)仍然还在。再看一下生成的SessionDaoImpl文件,会发现SetPK的声明已经发生改变:public virtual unsafe global::Com.Xsj.Crasheye.Session.Session SetPK (global::Java.Lang.Object entity, global::Java.Lang.Object id)。问题出在哪里呢?问题出在virtual声明上。
    根据C#的语法要求,SessionDaoImpl的SetPK方法要实现BaseDao.SetPK,除了方法名、参数和返回类型要一一匹配之外,方法声明里还要有override,现在是virtual自然是不对的。

    首先要移除virtual声明,添加如下代码可以解决:<attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]" name="final">true</attr>,但仅仅是移除virtual是不够的,因为Java的成员方法默认是虚方法,都是可以重写的,而C#的成员方法则默认是非虚的,是不可重写的,因此override在这里是必须的。
    但官方文档并没有给出这种情况应该如何解决,我经过一番摸索,得出了下述的解决方案:
    <attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]" name="visibility">public override</attr>将override关键字写在可见性声明里居然也可以,可见Metadata.xml本质就是一个模板文件;
    进行到这一步,问题又变化了CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.Dao.Impl.SessionDaoImpl.cs(67,67): Error CS0508: 'SessionDaoImpl.SetPK(Object, Object)': return type must be 'Object' to match overridden member 'BaseDao.SetPK(Object, Object)' (CS0508) (CrashEyeTestPlus),SessionDaoImpl.SetPK方法的返回类型不匹配。这个简单,添加如下代码即可:
    <attr path="/api/package[@name='com.xsj.crasheye.dao.impl']/class[@name='SessionDaoImpl']/method[@name='setPK' and count(parameter)=2 and parameter[1][@type='com.xsj.crasheye.session.Session'] and parameter[2][@type='java.lang.Long']]" name="managedReturn">Java.Lang.Object</attr>
    至此,关于SessionDaoImpl.SetPK的问题才算完全解决,但显然,SessionDaoImpl.SetPK方法的所有泛型的类型信息都被擦除了,我们在调用的时需要小心才行,保险起见,我们可以再封装一下SessionDaoImpl。

    其他的问题的原因和解决方案与上述大同小异,这里就不再赘述了,希望本文能对你有帮助。

    相关文章

      网友评论

        本文标题:解决Xamarin.Android绑定第三方库时类型丢失的问题(

        本文链接:https://www.haomeiwen.com/subject/zegasqtx.html