Пять, Android IPC механизмы (2) последовательности сериализуемых интерфейсов ---

Предупреждение: Данная статья является блоггером оригинал статьи, не могут быть воспроизведены без блоггеров допускается. https://blog.csdn.net/yz_cfm/article/details/90179887

Зачем использовать Serializable интерфейс:

    Во время нашего развития Android, Намерение часто используется для передачи данных, например, для перехода от активности A активности B, для передачи данных через умысел активность B:

Intent intent = new Intent(AActivity.this, BActivity.class);
intent.putExtra("name", "cfm");
intent.putExtra("dream", "open source");
startActivity(intent);

При этом удобно использовать, но Намерение только основные типы передачи данных, строковый тип и типы объектов можно сериализовать и десериализации . Поэтому , если мы хотим , чтобы общая категория объектов Намерение , передаваемых через определение наших собственных, мы должны позволить обычные объекты класса позволяют сериализации и операцию Десериализация. Иногда мы также должны приемлю сохранение в запоминающем устройстве или передаваться по сети с другими клиентами, на этот раз объект должен иметь возможность сериализации и де-сериализации может успешно работать.

    В Android можно SERIALIZABLE интерфейс и Parcelable для достижения цели интерфейса сериализации и де-сериализации. И мы здесь подробно останавливаться на принципах и использовать Serializable интерфейс.

Как использовать Serializable интерфейс:

    Java Сериализуемый предоставляется нам ряд интерфейсов, в пакете java.io.Serializable, является пустой интерфейс, который обеспечивает стандартную сериализации и десериализации объекта операции.

// java.io.Serializable
public interface Serializable {
}

    Кроме того, очень прост в использовании, требует лишь заявление, подобное следующему может автоматически идентифицировать процесс сериализации по умолчанию для сериализации класса. (Конечно, это утверждение не может определить конкретные различия в деталях позже.)

private static final long serialVersionUID = 1L;

    Операция объекта сериализации и десериализации очень просто, с помощью Java при условии , ObjectOutputStream в wirteObject () метод и из ObjectInputStream readObject () метод может быть:

EG1: Намерение передается через объект:

// User.java
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private int mId;
    private String mName;
    private String mDream;

    public User(int id, String name, String dream) {
        mId = id;
        mName = name;
        mDream = dream;
    }

    public int getId() {
        return mId;
    }

    public void setId(int id) {
        mId = id;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }

    public String getDream() {
        return mDream;
    }

    public void setDream(String dream) {
        mDream = dream;
    }
}
// AActivity.java
public class AActivity extends AppCompatActivity {

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

        Button btn = findViewById(R.id.first_btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(AActivity.this, BActivity.class);
                Serializable user = new User(1, "cfm", "open source");
                Log.d("cfmtest", "序列化的user: " + user.toString());
                intent.putExtra("userObject", user);
                startActivity(intent);
            }
        });
    }
}
// BActivity.java
public class BActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);
        User user = (User) getIntent().getSerializableExtra("userObject");
        Log.d("cfmtest", "反序列化的user: " + user.toString());
        Log.d("cfmtest", "ID: " + user.getId());
        Log.d("cfmtest", "Name: " + user.getName());
        Log.d("cfmtest", "Dream: " + user.getDream());
    }
}
// Log 打印信息
cfmtest: 序列化的user: com.cfm.serializabletest.User@1f02ac1
cfmtest: 反序列化的user: com.cfm.serializabletest.User@d5d2208
cfmtest: ID: 1
cfmtest: Name: cfm
cfmtest: Dream: open source

    Заключение: Мы можем видеть, что объект может быть передан через Intent, и добраться до объекта пользователя и передать содержимое объекта пользователя так же, но не тот же объект.

EG2: Объект пользователя устойчивость к устройству, а затем захват цели анти-последовательности.

public class AActivity extends AppCompatActivity {
    private ObjectOutputStream mOutputStream;
    private ObjectInputStream mInputStream;

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

        Button writeBtn = findViewById(R.id.write_btn);
        writeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    User user = new User(1, "cfm", "open source");
                    Log.d("cfmtest", "序列化的user: " + user.toString());
                    mOutputStream = new ObjectOutputStream(openFileOutput("test.txt", Context.MODE_PRIVATE));
                    mOutputStream.writeObject(user);
                    mOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        Button readBtn = findViewById(R.id.read_btn);
        readBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mInputStream = new ObjectInputStream(openFileInput("test.txt"));
                    User newUser = (User) mInputStream.readObject();
                    Log.d("cfmtest", "反序列化的user: " + newUser.toString());
                    Log.d("cfmtest", "ID: " + newUser.getId());
                    Log.d("cfmtest", "Name: " + newUser.getName());
                    Log.d("cfmtest", "Dream: " + newUser.getDream());
                    mInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

// output
cfmtest: 序列化的user: com.cfm.serializabletest.User@630369f
cfmtest: 反序列化的user: com.cfm.serializabletest.User@b46c484
cfmtest: ID: 1
cfmtest: Name: cfm
cfmtest: Dream: open source

    Вывод: Опять же, мы можем видеть последовательность целевого объекта и конечную последовательность анти-а не тот же объект, но один и тот же контент.

Подробное serialVersionUID:

    serialVersionUID используется для оказания помощи сериализации и десериализации процесса, и только данные в текущем классе serialVersionUID serialVersionUID, чтобы быть в состоянии десериализован обычно основывается на принципе последовательности. Если мы не будем вручную указать значение serialVersionUID, то система будет автоматически значение хеш-функции текущего класса, присвоенный ему в качестве serialVersionUID, и это значение хеш-функции (например, добавление или удаление некоторых переменных-членов) с изменением текущего изменения класса , так что это может легко привести к объекту при сериализации и десериализации serialVersionUID текущий класс serialVersionUID не то же самое, что приводит к недостаточности десериализации. Конечно, даже если мы вручную указать значение serialVersionUID, но текущий класс структуры класса изменил нетрадиционный характер (например, изменение имени класса, изменить тип переменного члена, и т.д.), даже если serialVersionUID проверено, но анти-последовательность процесс или потерпит неудачу, потому что структура класса имеет разрушительные изменения, вы не можете восстановить объект новой структуры класса от старой версии данных.

    serialVersionUID рабочий механизм: последовательность времени система будет записывать последовательность serialVersionUID текущий класс документов (и, возможно, других средств массовой информации), время десериализации, когда система будет проверять текущие файлы классов и serialVersionUID serialVersionUID это то же самое, если он показывает ту же версию последовательности класса и текущий класс такой же, на этот раз может быть успешно десериализацией, в противном случае она показывает текущий класс и классы, которые сериализации претерпели определенные изменения по сравнению, например, в качестве членов число переменных, тип изменения может иметь место, на этот раз не нормальная последовательность анти.

    Как правило, у нас есть два способа указать классы собой последовательность serialVersionUID, один непосредственно обозначен 1L, другой прямой явное значение хэш-функции, порожденное текущего присвоения класса к нему. Там нет ничего принципиально отличается двумя способами, вы можете достичь той же цели.

    Примечание:

    1. Статические переменные-члены, принадлежащие к классу не относятся к объекту, так что он не будет участвовать в процессе сериализации.

    2. переходного ключевое слово переменной модифицированы члена не участвуют в процессе секвенирования.

Сериализуемый принцип анализа:

    Как анализ? Ничего больше, чем метод анализа не полный исходный код сериализации и де-сериализации процедура вызова вещь:

    Посмотрите процесс сериализации: ObjectOutputStream.writeObject ():

public final void writeObject(Object obj) throws IOException {
    if (enableOverride) {          // 1
        writeObjectOverride(obj);
        return;
    }

    try {
        writeObject0(obj, false);
    } catch (IOException ex) {
        ...
    }
}
public ObjectOutputStream(OutputStream out) throws IOException {
    ...
    enableOverride = false;   // 所以 1 不调用
    ...
}

    Следующий вызов: writeObject0 (объект, ложь);

private void writeObject0(Object obj, boolean unshared)
        throws IOException
    {
            ...
            Class<?> cl = obj.getClass();   // 通过反射得到 obj 的 Class 对象
            ObjectStreamClass desc;

            Class repCl;
            desc = ObjectStreamClass.lookup(cl, true); // 返回给定类的类描述符

            if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
            // END Android-changed:  Make Class and ObjectStreamClass replaceable.
            } else if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);  // 我们这里是实现 Serializable 接口的对象,所以跳转到这个方法
            } else {
                ...
            }
        } finally {
            ...
        }
    }

    Затем перейти к: writeOrdinaryObject (объект, убывание, неразделенная)

private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) throws IOException
{
    ...
    try {
        ...
        if (desc.isExternalizable() && !desc.isProxy()) { 
            writeExternalData((Externalizable) obj);
        } else {
            writeSerialData(obj, desc);
        }
   ...
}

    Метод внутреннего writeOrdinaryObject () текущего класс определяется выше реализует Serializable Externalizable интерфейса или интерфейсы, наша текущая реализация является Сериализуемым интерфейсом класса, а затем перейти к writeSerialData (OBJ, по убыванию) Метод:

private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException
{
    ...
    if (slotDesc.hasWriteObjectMethod()) {
            ...
            try {
                curContext = new SerialCallbackContext(obj, slotDesc);
                bout.setBlockDataMode(true);
                slotDesc.invokeWriteObject(obj, this);
                bout.setBlockDataMode(false);
                bout.writeByte(TC_ENDBLOCKDATA);
            } ...
        } else {
            defaultWriteFields(obj, slotDesc);
        }
}
writeObjectMethod = getPrivateMethod(cl, "writeObject", new Class<?>[] { ObjectOutputStream.class }, Void.TYPE);  // 通过反射获取 writeObject(ObjectOutputStream) 方法
boolean hasWriteObjectMethod() { return (writeObjectMethod != null); }  // 如果当前类中有 writeObject(ObjectOutputStream) 方法,则返回 ture,否则返回 false
void invokeWriteObject(Object obj, ObjectOutputStream out) throws IOException, UnsupportedOperationException
{
    if (writeObjectMethod != null) {
        try {
            writeObjectMethod.invoke(obj, new Object[]{ out });  // 通过反射调用当前类中的 writeObject(ObjectOutputStream)
        }
        ...
    }
}

    Здесь вы можете увидеть текущий класс определяется, есть ли способ writeObject (ObjectOutputStream) путем вызова метода hasWriteObjectMethod (). Метод Если это так, вызовите invokeWriteObject () вызывается, если не вызывается метод defaultWriteFields окончательной обработки вызовов (OBJ, slotDesc) в текущем классе writeObject (ObjectOutputStream), путем отражения.

    Точно так же, ObjectInputStream.readObject () анализ, как если бы readObject (ObjectInputStream) Метод текущего класса называются или метода defaultReadFields вызова (OBJ, slotDesc).

    Подводя итог, мы можем видеть , что процесс сериализации по умолчанию может быть изменен, до тех пор , как они определены в текущем классе  writeObject (ObjectOutputStream) и  readObject (ObjectInputStream) может быть!

рекомендация

отblog.csdn.net/yz_cfm/article/details/90179887